Compare commits

...

16 Commits

Author SHA1 Message Date
Spythere 13aa1acc54 Merge pull request #24 from Spythere/development
Wersja 1.8.4
2024-04-10 16:32:03 +02:00
Spythere 966181c977 fix: wiki list table height 2024-04-10 16:25:11 +02:00
Spythere 06eb4bc607 fix: button bg color 2024-04-10 16:18:32 +02:00
Spythere c07a16d245 fix: main container responsiveness 2024-04-10 16:12:08 +02:00
Spythere 7e67e34526 chore: wiki list tab sizing 2024-04-10 15:35:51 +02:00
Spythere 6cfc535dec chore: packages update 2024-04-09 16:32:23 +02:00
Spythere d072692db7 chore: pwa vehicles cache 2024-04-09 14:47:56 +02:00
Spythere 6255efd98d fix: http url 2024-04-08 23:44:37 +02:00
Spythere 9c59c30f12 chore(api): url changes 2024-04-08 23:40:24 +02:00
Spythere 31302cc053 chorse: changed api data to stored locally 2024-04-08 22:20:18 +02:00
Spythere 26fd0c67e4 feat: team & sponsor restrictions added 2024-04-07 22:00:09 +02:00
Spythere d98ec94a66 chore: service worker changes, got rid of excessive console logs 2024-04-07 18:54:03 +02:00
Spythere 28485cc28c real stock card exit btn; hotfixes 2024-04-06 16:19:04 +02:00
Spythere b14c7a9502 cargo count sorting fix 2024-04-06 16:08:05 +02:00
Spythere 90e78e5ac5 wiki list lazy load (intersection obs.) 2024-04-06 15:45:39 +02:00
Spythere 27f02e2c2b api & naming changes 2024-04-06 14:54:54 +02:00
32 changed files with 516 additions and 480 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"hosting": { "hosting": {
"public": "dist", "public": "dist",
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"], "ignore": [],
"rewrites": [ "rewrites": [
{ {
"source": "**", "source": "**",
+20 -24
View File
@@ -1,18 +1,18 @@
{ {
"name": "pojazdownik", "name": "pojazdownik",
"version": "1.7.4", "version": "1.8.4",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "pojazdownik", "name": "pojazdownik",
"version": "1.7.4", "version": "1.8.4",
"dependencies": { "dependencies": {
"axios": "^1.4.0", "axios": "^1.4.0",
"pinia": "^2.0.17", "pinia": "^2.0.17",
"prettier": "^3.0.3", "prettier": "^3.0.3",
"vue": "^3.2.37", "vue": "^3.2.37",
"vue-i18n": "9" "vue-i18n": "9.11.0"
}, },
"devDependencies": { "devDependencies": {
"@rushstack/eslint-patch": "^1.3.3", "@rushstack/eslint-patch": "^1.3.3",
@@ -2028,13 +2028,12 @@
"license": "BSD-3-Clause" "license": "BSD-3-Clause"
}, },
"node_modules/@intlify/core-base": { "node_modules/@intlify/core-base": {
"version": "9.9.1", "version": "9.11.0",
"resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.9.1.tgz", "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.11.0.tgz",
"integrity": "sha512-qsV15dg7jNX2faBRyKMgZS8UcFJViWEUPLdzZ9UR0kQZpFVeIpc0AG7ZOfeP7pX2T9SQ5jSiorq/tii9nkkafA==", "integrity": "sha512-cveOqAstjLZIiyatcP/HrzrQ87cZI8ScPQna3yvoM8zjcjcIRK1MRvmxUNlPdg0rTNJMZw7rixPVM58O5aHVPA==",
"license": "MIT",
"dependencies": { "dependencies": {
"@intlify/message-compiler": "9.9.1", "@intlify/message-compiler": "9.11.0",
"@intlify/shared": "9.9.1" "@intlify/shared": "9.11.0"
}, },
"engines": { "engines": {
"node": ">= 16" "node": ">= 16"
@@ -2044,12 +2043,11 @@
} }
}, },
"node_modules/@intlify/message-compiler": { "node_modules/@intlify/message-compiler": {
"version": "9.9.1", "version": "9.11.0",
"resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.9.1.tgz", "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.11.0.tgz",
"integrity": "sha512-zTvP6X6HeumHOXuAE1CMMsV6tTX+opKMOxO1OHTCg5N5Sm/F7d8o2jdT6W6L5oHUsJ/vvkGefHIs7Q3hfowmsA==", "integrity": "sha512-x31Gl7cscnoI4UUY1yaIy8e7vVMVW1VVlTXZz4SIHKqoSEUkfmgqK8NAx1e7RcoHEbICR7uyCbud0ZL1s4OGXQ==",
"license": "MIT",
"dependencies": { "dependencies": {
"@intlify/shared": "9.9.1", "@intlify/shared": "9.11.0",
"source-map-js": "^1.0.2" "source-map-js": "^1.0.2"
}, },
"engines": { "engines": {
@@ -2060,10 +2058,9 @@
} }
}, },
"node_modules/@intlify/shared": { "node_modules/@intlify/shared": {
"version": "9.9.1", "version": "9.11.0",
"resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.9.1.tgz", "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.11.0.tgz",
"integrity": "sha512-b3Pta1nwkz5rGq434v0psHwEwHGy1pYCttfcM22IE//K9owbpkEvFptx9VcuRAxjQdrO2If249cmDDjBu5wMDA==", "integrity": "sha512-KHSNgi7sRjmSm7aD8QH8WFt9VfKaekJuJ473opbJlkGY3EDnDUU8ikIhG8PbasQbgNvbY3m3tWNGqk2omIdwMA==",
"license": "MIT",
"engines": { "engines": {
"node": ">= 16" "node": ">= 16"
}, },
@@ -7624,13 +7621,12 @@
} }
}, },
"node_modules/vue-i18n": { "node_modules/vue-i18n": {
"version": "9.9.1", "version": "9.11.0",
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.9.1.tgz", "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.11.0.tgz",
"integrity": "sha512-xyQ4VspLdNSPTKBFBPWa1tvtj+9HuockZwgFeD2OhxxXuC2CWeNvV4seu2o9+vbQOyQbhAM5Ez56oxUrrnTWdw==", "integrity": "sha512-vU4gY6lu8Pdfs9BgKGiDAJmFDf88cceR47KcSB0VW4xJzUrXR/7qwqM7A8dQ2nedhoIDxoOm5Ro4pFd2KvJqbA==",
"license": "MIT",
"dependencies": { "dependencies": {
"@intlify/core-base": "9.9.1", "@intlify/core-base": "9.11.0",
"@intlify/shared": "9.9.1", "@intlify/shared": "9.11.0",
"@vue/devtools-api": "^6.5.0" "@vue/devtools-api": "^6.5.0"
}, },
"engines": { "engines": {
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "pojazdownik", "name": "pojazdownik",
"version": "1.8.3.1", "version": "1.8.4",
"private": true, "private": true,
"type": "module", "type": "module",
"scripts": { "scripts": {
@@ -16,7 +16,7 @@
"pinia": "^2.0.17", "pinia": "^2.0.17",
"prettier": "^3.0.3", "prettier": "^3.0.3",
"vue": "^3.2.37", "vue": "^3.2.37",
"vue-i18n": "9" "vue-i18n": "9.11.0"
}, },
"devDependencies": { "devDependencies": {
"@rushstack/eslint-patch": "^1.3.3", "@rushstack/eslint-patch": "^1.3.3",
+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="white" width="18px" height="18px"><path d="M0 0h24v24H0z" fill="none"/><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>

After

Width:  |  Height:  |  Size: 256 B

Before

Width:  |  Height:  |  Size: 1020 B

After

Width:  |  Height:  |  Size: 1020 B

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Before

Width:  |  Height:  |  Size: 932 B

After

Width:  |  Height:  |  Size: 932 B

Before

Width:  |  Height:  |  Size: 953 B

After

Width:  |  Height:  |  Size: 953 B

+2 -2
View File
@@ -8,8 +8,8 @@
</template> </template>
</i18n-t> </i18n-t>
<div class="text--grayed" v-if="store.vehiclesAPIData"> <div class="text--grayed" v-if="store.vehiclesData">
{{ $t('footer.version-check', { version: store.vehiclesAPIData.simulatorVersion }) }} {{ $t('footer.version-check', { version: store.vehiclesData.simulatorVersion }) }}
</div> </div>
<div> <div>
+3 -4
View File
@@ -35,15 +35,14 @@ main {
background-color: darken($color: $bgColor, $amount: 5); background-color: darken($color: $bgColor, $amount: 5);
border-radius: 1em; border-radius: 1em;
min-height: 950px;
padding: 1em; padding: 1em;
} }
@media screen and (max-width: $breakpointMd) { @media screen and (max-width: $breakpointMd) {
main { main {
display: flex; display: block;
flex-direction: column;
grid-template-columns: 1fr;
grid-template-rows: 1fr;
} }
} }
</style> </style>
+15 -13
View File
@@ -9,8 +9,8 @@
{{ $t('realstock.title') }} {{ $t('realstock.title') }}
<a href="https://td2.info.pl/profile/?u=17708" target="_blank">Railtrains997</a> <a href="https://td2.info.pl/profile/?u=17708" target="_blank">Railtrains997</a>
</h1> </h1>
<button class="btn exit-btn" @click="store.isRealStockListCardOpen = false"> <button class="btn action-exit" @click="store.isRealStockListCardOpen = false">
&Cross; <img src="/images/icon-exit.svg" alt="" />
</button> </button>
</div> </div>
@@ -47,7 +47,7 @@
</option> </option>
</datalist> </datalist>
<button class="btn" @click="resetStockFilters"> <button class="btn action-reset" @click="resetStockFilters">
{{ $t('realstock.action-reset') }} {{ $t('realstock.action-reset') }}
</button> </button>
</div> </div>
@@ -100,13 +100,13 @@ import { useStore } from '../../store';
import imageMixin from '../../mixins/imageMixin'; import imageMixin from '../../mixins/imageMixin';
import stockMixin from '../../mixins/stockMixin'; import stockMixin from '../../mixins/stockMixin';
import { IRealComposition } from '../../types'; import { IRealComposition, VehicleGroupType } from '../../types';
function getVehicleType(stockType: string) { function getVehicleType(stockType: string): VehicleGroupType {
if (/^E/.test(stockType)) return 'loco-e'; if (/^E/.test(stockType)) return 'loco-electric';
if (/^S/.test(stockType)) return 'loco-s'; if (/^S/.test(stockType)) return 'loco-diesel';
return 'car-passenger'; return 'wagon-passenger';
} }
export default defineComponent({ export default defineComponent({
@@ -222,13 +222,15 @@ export default defineComponent({
<style lang="scss" scoped> <style lang="scss" scoped>
@import '../../styles/global.scss'; @import '../../styles/global.scss';
.exit-btn { .action-exit {
font-size: 1.2em; display: flex;
margin: 0.25em 0; background-color: #333;
border-radius: 0.25em;
padding: 0.5em;
} }
.btn { .action-reset {
background-color: #444; background-color: #333;
} }
.card_content { .card_content {
+20 -19
View File
@@ -9,7 +9,7 @@
v-for="locoType in locomotiveTypeList" v-for="locoType in locomotiveTypeList"
:key="locoType.id" :key="locoType.id"
class="btn btn--choice" class="btn btn--choice"
:data-selected="locoType.id == store.chosenLocoPower" :data-selected="locoType.id == store.chosenLocoGroup"
@click="selectLocoType(locoType.id)" @click="selectLocoType(locoType.id)"
> >
{{ $t(`inputs.${locoType.id}`) }} {{ $t(`inputs.${locoType.id}`) }}
@@ -28,7 +28,7 @@
{{ $t('inputs.input-vehicle') }} {{ $t('inputs.input-vehicle') }}
</option> </option>
<option v-for="loco in locoOptions" :value="loco" :key="loco.type"> <option v-for="loco in locoOptions" :value="loco" :key="loco.type">
{{ loco.type }}<b v-if="loco.isSponsorsOnly">*</b> {{ loco.type }}<b v-if="loco.restrictions['sponsorOnly']">*</b>
</option> </option>
</select> </select>
</div> </div>
@@ -39,7 +39,7 @@
v-for="carType in carTypeList" v-for="carType in carTypeList"
:key="carType.id" :key="carType.id"
class="btn btn--choice" class="btn btn--choice"
:data-selected="carType.id == store.chosenCarUseType" :data-selected="carType.id == store.chosenCarGroup"
@click="selectCarWagonType(carType.id)" @click="selectCarWagonType(carType.id)"
> >
{{ $t(`inputs.${carType.id}`) }} {{ $t(`inputs.${carType.id}`) }}
@@ -59,7 +59,7 @@
</option> </option>
<option v-for="car in carOptions" :value="car" :key="car.type"> <option v-for="car in carOptions" :value="car" :key="car.type">
{{ car.type }}<b v-if="car.isSponsorsOnly">*</b> {{ car.type }}<b v-if="car.restrictions['sponsorOnly']">*</b>
</option> </option>
</select> </select>
</div> </div>
@@ -70,7 +70,7 @@
id="cargo-select" id="cargo-select"
:disabled=" :disabled="
(store.chosenCar && !store.chosenCar.loadable) || (store.chosenCar && !store.chosenCar.loadable) ||
(store.chosenCar && store.chosenCar.useType == 'car-passenger') || (store.chosenCar && store.chosenCar.group == 'wagon-passenger') ||
!store.chosenCar !store.chosenCar
" "
data-select="cargo" data-select="cargo"
@@ -123,6 +123,7 @@ import imageMixin from '../../mixins/imageMixin';
import { useStore } from '../../store'; import { useStore } from '../../store';
import stockPreviewMixin from '../../mixins/stockPreviewMixin'; import stockPreviewMixin from '../../mixins/stockPreviewMixin';
import stockMixin from '../../mixins/stockMixin'; import stockMixin from '../../mixins/stockMixin';
import { LocoGroupType, WagonGroupType } from '../../types';
export default defineComponent({ export default defineComponent({
mixins: [imageMixin, stockPreviewMixin, stockMixin], mixins: [imageMixin, stockPreviewMixin, stockMixin],
@@ -131,33 +132,33 @@ export default defineComponent({
store: useStore(), store: useStore(),
locomotiveTypeList: [ locomotiveTypeList: [
{ {
id: 'loco-e', id: 'loco-electric',
desc: 'ELEKTRYCZNE', desc: 'ELEKTRYCZNE',
}, },
{ {
id: 'loco-s', id: 'loco-diesel',
desc: 'SPALINOWE', desc: 'SPALINOWE',
}, },
{ {
id: 'loco-ezt', id: 'unit-electric',
desc: 'ELEKTR. ZESPOŁY TRAKCYJNE', desc: 'ELEKTR. ZESPOŁY TRAKCYJNE',
}, },
{ {
id: 'loco-szt', id: 'unit-diesel',
desc: 'SPAL. ZESPOŁY TRAKCYJNE', desc: 'SPAL. ZESPOŁY TRAKCYJNE',
}, },
], ] as { id: LocoGroupType; desc: string }[],
carTypeList: [ carTypeList: [
{ {
id: 'car-passenger', id: 'wagon-passenger',
desc: 'PASAŻERSKIE', desc: 'PASAŻERSKIE',
}, },
{ {
id: 'car-cargo', id: 'wagon-freight',
desc: 'TOWAROWE', desc: 'TOWAROWE',
}, },
], ] as { id: WagonGroupType; desc: string }[],
}), }),
computed: { computed: {
@@ -165,14 +166,14 @@ export default defineComponent({
return this.store.locoDataList return this.store.locoDataList
.slice() .slice()
.sort((a, b) => (a.type > b.type ? 1 : -1)) .sort((a, b) => (a.type > b.type ? 1 : -1))
.filter((loco) => loco.power == this.store.chosenLocoPower); .filter((loco) => loco.group == this.store.chosenLocoGroup);
}, },
carOptions() { carOptions() {
return this.store.carDataList return this.store.carDataList
.slice() .slice()
.sort((a, b) => (a.type > b.type ? 1 : -1)) .sort((a, b) => (a.type > b.type ? 1 : -1))
.filter((car) => car.useType == this.store.chosenCarUseType); .filter((car) => car.group == this.store.chosenCarGroup);
}, },
}, },
@@ -209,14 +210,14 @@ export default defineComponent({
this.store.stockList[this.store.chosenStockListIndex] = stockObject; this.store.stockList[this.store.chosenStockListIndex] = stockObject;
}, },
selectLocoType(locoTypeId: string) { selectLocoType(locoGroupType: LocoGroupType) {
this.store.chosenLocoPower = locoTypeId; this.store.chosenLocoGroup = locoGroupType;
this.store.chosenVehicle = this.locoOptions[0]; this.store.chosenVehicle = this.locoOptions[0];
this.store.chosenLoco = this.locoOptions[0]; this.store.chosenLoco = this.locoOptions[0];
}, },
selectCarWagonType(carWagonTypeId: string) { selectCarWagonType(wagonGroupType: WagonGroupType) {
this.store.chosenCarUseType = carWagonTypeId; this.store.chosenCarGroup = wagonGroupType;
this.store.chosenVehicle = this.carOptions[0]; this.store.chosenVehicle = this.carOptions[0];
this.store.chosenCar = this.carOptions[0]; this.store.chosenCar = this.carOptions[0];
this.store.chosenCargo = null; this.store.chosenCargo = null;
-1
View File
@@ -51,7 +51,6 @@ export default {
.logo-section { .logo-section {
grid-row: 1; grid-row: 1;
grid-column: 1; grid-column: 1;
margin-bottom: 1.5em;
display: flex; display: flex;
align-items: center; align-items: center;
+42 -33
View File
@@ -1,26 +1,27 @@
<template> <template>
<section class="train-image-section"> <section class="train-image-section">
<div class="train-image__content" :class="{ sponsor: store.chosenVehicle?.isSponsorsOnly }"> <div class="image-wrapper">
<img <img
tabindex="0"
:src=" :src="
store.chosenVehicle store.chosenVehicle
? getThumbnailURL(store.chosenVehicle.type, 'small') ? getThumbnailURL(store.chosenVehicle.type, 'small')
: '/images/placeholder.jpg' : '/images/placeholder.jpg'
" "
tabindex="0"
:data-sponsor-only="store.chosenVehicle?.restrictions.sponsorOnly"
:data-team-only="store.chosenVehicle?.restrictions.teamOnly"
@click="onImageClick" @click="onImageClick"
@keydown.enter="onImageClick" @keydown.enter="onImageClick"
@error="onImageError" @error="onImageError"
type="image/jpeg"
/> />
</div> </div>
<div class="train-image__info" v-if="store.chosenVehicle"> <div class="image-info" v-if="store.chosenVehicle">
<b class="text--accent">{{ store.chosenVehicle.type }}</b> &bull; <b class="text--accent">{{ store.chosenVehicle.type }}</b> &bull;
<b style="color: #ccc"> <b style="color: #ccc">
{{ {{
$t( $t(
`preview.${isLocomotive(store.chosenVehicle) ? store.chosenVehicle.power : store.chosenVehicle.useType}` `preview.${isLocomotive(store.chosenVehicle) ? store.chosenVehicle.group : store.chosenVehicle.group}`
) )
}} }}
</b> </b>
@@ -37,30 +38,34 @@
<div v-else> <div v-else>
{{ {{
store.chosenVehicle.useType == 'car-cargo' store.chosenVehicle.group == 'wagon-freight'
? $t(`usage.${store.chosenVehicle.constructionType}`) ? $t(`usage.${store.chosenVehicle.constructionType}`)
: `${$t('preview.construction')} ${store.chosenVehicle.constructionType}` : `${$t('preview.construction')} ${store.chosenVehicle.constructionType}`
}} }}
</div> </div>
<b style="color: salmon" v-if="store.chosenVehicle.isSponsorsOnly">{{ <b style="color: salmon" v-if="store.chosenVehicle.restrictions['sponsorOnly']">{{
$t('preview.sponsor-only', [ $t('preview.sponsor-only', [
new Date(store.chosenVehicle.sponsorsOnlyTimestamp).toLocaleDateString( new Date(store.chosenVehicle.restrictions['sponsorOnly']).toLocaleDateString(
$i18n.locale == 'pl' ? 'pl-PL' : 'en-GB' $i18n.locale == 'pl' ? 'pl-PL' : 'en-GB'
), ),
]) ])
}}</b> }}</b>
<b style="color: gold" v-if="store.chosenVehicle.restrictions['teamOnly']">{{
$t('preview.team-only')
}}</b>
</div> </div>
</div> </div>
<div class="train-image__info" v-else>{{ $t('preview.desc') }}</div> <div class="image-info" v-else>{{ $t('preview.desc') }}</div>
</section> </section>
</template> </template>
<script lang="ts"> <script lang="ts">
import { computed, defineComponent } from 'vue'; import { computed, defineComponent } from 'vue';
import { useStore } from '../../store'; import { useStore } from '../../store';
import { isLocomotive } from '../../utils/vehicleUtils'; import { isTractionUnit } from '../../utils/vehicleUtils';
import { ILocomotive, IVehicle } from '../../types'; import { ILocomotive, IVehicle } from '../../types';
import imageMixin from '../../mixins/imageMixin'; import imageMixin from '../../mixins/imageMixin';
@@ -103,7 +108,7 @@ export default defineComponent({
}, },
isLocomotive(vehicle: IVehicle): vehicle is ILocomotive { isLocomotive(vehicle: IVehicle): vehicle is ILocomotive {
return isLocomotive(vehicle); return isTractionUnit(vehicle);
}, },
onImageClick(e: Event) { onImageClick(e: Event) {
@@ -121,7 +126,7 @@ export default defineComponent({
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import '../../styles/global.scss'; @import '../../styles/global';
.train-image-section { .train-image-section {
display: flex; display: flex;
@@ -135,24 +140,41 @@ export default defineComponent({
height: 22em; height: 22em;
} }
.train-image { img {
&__content {
&.sponsor img {
border: 1px solid salmon;
}
img {
max-width: 380px; max-width: 380px;
width: 100%; width: 100%;
height: 100%; height: 100%;
border: 1px solid white; border: 1px solid white;
cursor: zoom-in; cursor: zoom-in;
&[data-sponsor-only='true'] {
border: 1px solid $sponsorColor;
} }
&[data-team-only='true'] {
border: 1px solid $teamColor;
} }
} }
.train-image__info { // .train-image {
// &__content {
// &.sponsor img {
// border: 1px solid salmon;
// }
// img {
// max-width: 380px;
// width: 100%;
// height: 100%;
// border: 1px solid white;
// cursor: zoom-in;
// }
// }
// }
.image-info {
font-size: 1.1em; font-size: 1.1em;
padding: 0.5em; padding: 0.5em;
margin: 0.5em auto; margin: 0.5em auto;
@@ -165,19 +187,6 @@ export default defineComponent({
font-weight: bold; font-weight: bold;
} }
// Transition animations
.img-message-anim {
&-enter-from,
&-leave-to {
opacity: 0;
}
&-enter-active,
&-leave-active {
transition: opacity 75ms ease-in 100ms;
}
}
@media screen and (max-width: $breakpointMd) { @media screen and (max-width: $breakpointMd) {
.train-image-section { .train-image-section {
justify-content: center; justify-content: center;
+5 -6
View File
@@ -119,11 +119,10 @@ import { useStore } from '../../store';
import stockMixin from '../../mixins/stockMixin'; import stockMixin from '../../mixins/stockMixin';
import { ICargo, ICarWagon, IStock } from '../../types'; import { ICargo, ICarWagon, IStock } from '../../types';
import warningsMixin from '../../mixins/warningsMixin';
export default defineComponent({ export default defineComponent({
name: 'stock-generator', name: 'stock-generator',
mixins: [stockMixin, warningsMixin], mixins: [stockMixin],
data() { data() {
return { return {
@@ -150,9 +149,9 @@ export default defineComponent({
}, },
computedCargoData() { computedCargoData() {
if (!this.store.vehiclesAPIData?.generator.cargo) return []; if (!this.store.vehiclesData?.generator.cargo) return [];
const cargoGeneratorData = this.store.vehiclesAPIData.generator.cargo; const cargoGeneratorData = this.store.vehiclesData.generator.cargo;
return Object.keys(cargoGeneratorData) return Object.keys(cargoGeneratorData)
.sort((v1, v2) => this.$t(`cargo.${v1}`).localeCompare(this.$t(`cargo.${v2}`))) .sort((v1, v2) => this.$t(`cargo.${v1}`).localeCompare(this.$t(`cargo.${v2}`)))
@@ -196,7 +195,7 @@ export default defineComponent({
generateStock(empty = false) { generateStock(empty = false) {
const generatedChosenStockList = this.chosenCargoTypes.reduce( const generatedChosenStockList = this.chosenCargoTypes.reduce(
(acc, type) => { (acc, type) => {
this.store.vehiclesAPIData?.generator.cargo[type] this.store.vehiclesData?.generator.cargo[type]
.filter((c) => !this.excludedCarTypes.includes(c.split(':')[0])) .filter((c) => !this.excludedCarTypes.includes(c.split(':')[0]))
.forEach((c) => { .forEach((c) => {
const [type, cargoType] = c.split(':'); const [type, cargoType] = c.split(':');
@@ -290,7 +289,7 @@ export default defineComponent({
this.store.chosenLoco = null; this.store.chosenLoco = null;
this.store.chosenCargo = null; this.store.chosenCargo = null;
if (c) this.store.chosenCarUseType = c?.useType; if (c) this.store.chosenCarGroup = c?.group;
}, },
toggleCargoChosen(cargoType: string, vehicles: string[]) { toggleCargoChosen(cargoType: string, vehicles: string[]) {
+110 -57
View File
@@ -115,20 +115,29 @@
</Checkbox> </Checkbox>
</div> </div>
<div class="stock_warnings" v-if="stockHasWarnings"> <div class="stock_warnings" v-if="hasAnyWarnings">
<div class="warning" v-if="locoNotSuitable"> <div class="warning" v-if="locoNotSuitable">
(!) {{ $t('stocklist.warning-not-suitable') }} (!) {{ $t('stocklist.warning-not-suitable') }}
</div> </div>
<div class="warning" v-if="trainTooLong && store.isTrainPassenger"> <div class="warning" v-if="lengthExceeded && store.isTrainPassenger">
(!) {{ $t('stocklist.warning-passenger-too-long') }} (!) {{ $t('stocklist.warning-passenger-too-long') }}
</div> </div>
<div class="warning" v-if="trainTooLong && !store.isTrainPassenger"> <div class="warning" v-if="lengthExceeded && !store.isTrainPassenger">
(!) {{ $t('stocklist.warning-freight-too-long') }} (!) {{ $t('stocklist.warning-freight-too-long') }}
</div> </div>
<div class="warning" v-if="trainTooHeavy"> <div class="warning" v-if="teamOnlyVehicles.length > 0">
(!)
{{
$t('stocklist.warning-team-only-vehicle', [
teamOnlyVehicles.map((v) => v.type).join(', '),
])
}}
</div>
<div class="warning" v-if="weightExceeded">
(!) (!)
<i18n-t keypath="stocklist.warning-too-heavy"> <i18n-t keypath="stocklist.warning-too-heavy">
<template #href> <template #href>
@@ -142,7 +151,7 @@
</i18n-t> </i18n-t>
</div> </div>
<div class="warning" v-if="tooManyLocomotives"> <div class="warning" v-if="locoCountExceeded">
{{ $t('stocklist.warning-too-many-locos') }} {{ $t('stocklist.warning-too-many-locos') }}
</div> </div>
</div> </div>
@@ -150,12 +159,13 @@
<StockThumbnails :onListItemClick="onListItemClick" /> <StockThumbnails :onListItemClick="onListItemClick" />
<!-- Stock list --> <!-- Stock list -->
<ul ref="stock_list"> <div class="list-wrapper">
<li v-if="stockIsEmpty" class="list-empty"> <div v-if="stockIsEmpty" class="list-empty">
<div class="stock-info">{{ $t('stocklist.list-empty') }}</div> <div class="stock-info">{{ $t('stocklist.list-empty') }}</div>
</li> </div>
<TransitionGroup name="stock-list-anim" v-else> <ul v-else>
<transition-group name="stock-list-anim">
<li <li
v-for="(stock, i) in store.stockList" v-for="(stock, i) in store.stockList"
:key="stock.id" :key="stock.id"
@@ -175,27 +185,34 @@
@dragover="allowDrop" @dragover="allowDrop"
draggable="true" draggable="true"
> >
<span class="stock-info__no" :data-selected="i == store.chosenStockListIndex"> <span class="stock-info-no" :data-selected="i == store.chosenStockListIndex">
<span v-if="i == store.chosenStockListIndex">&bull;&nbsp;</span> <span v-if="i == store.chosenStockListIndex">&bull;&nbsp;</span>
{{ i + 1 }}. {{ i + 1 }}.
</span> </span>
<span class="stock-info__type" :class="{ sponsor: stock.isSponsorsOnly }"> <span
class="stock-info-type"
:data-sponsor-only="stock.restrictions.sponsorOnly"
:data-team-only="stock.restrictions.teamOnly"
>
{{ stock.isLoco ? stock.type : getCarSpecFromType(stock.type) }} {{ stock.isLoco ? stock.type : getCarSpecFromType(stock.type) }}
</span> </span>
<span class="stock-info__cargo" v-if="stock.cargo"> <span class="stock-info-cargo" v-if="stock.cargo">
{{ stock.cargo.id }} {{ stock.cargo.id }}
</span> </span>
<span class="stock-info__length">{{ stock.length }}m</span>
<span class="stock-info__mass" <span class="stock-info-length">{{ stock.length }}m</span>
>{{ ((stock.weight + (stock.cargo?.weight ?? 0)) / 1000).toFixed(1) }}t</span
> <span class="stock-info-mass">
<span class="stock-info__speed">{{ stock.maxSpeed }}km/h</span> {{ ((stock.weight + (stock.cargo?.weight ?? 0)) / 1000).toFixed(1) }}t
</span>
<span class="stock-info-speed">{{ stock.maxSpeed }}km/h</span>
</div> </div>
</li> </li>
</TransitionGroup> </transition-group>
</ul> </ul>
</div>
</section> </section>
</template> </template>
@@ -204,7 +221,6 @@ import { defineComponent } from 'vue';
import { useStore } from '../../store'; import { useStore } from '../../store';
import warningsMixin from '../../mixins/warningsMixin';
import imageMixin from '../../mixins/imageMixin'; import imageMixin from '../../mixins/imageMixin';
import stockPreviewMixin from '../../mixins/stockPreviewMixin'; import stockPreviewMixin from '../../mixins/stockPreviewMixin';
import StockThumbnails from '../utils/StockThumbnails.vue'; import StockThumbnails from '../utils/StockThumbnails.vue';
@@ -215,7 +231,7 @@ export default defineComponent({
name: 'stock-list', name: 'stock-list',
components: { StockThumbnails, Checkbox }, components: { StockThumbnails, Checkbox },
mixins: [warningsMixin, imageMixin, stockMixin, stockPreviewMixin], mixins: [imageMixin, stockMixin, stockPreviewMixin],
setup() { setup() {
const store = useStore(); const store = useStore();
@@ -233,6 +249,12 @@ export default defineComponent({
}), }),
computed: { computed: {
chosenRealComposition() {
const currentStockString = this.store.stockList.map((s) => s.type).join(';');
return this.store.realCompositionList.find((rc) => rc.stockString == currentStockString);
},
stockString() { stockString() {
if (this.store.stockList.length == 0) return ''; if (this.store.stockList.length == 0) return '';
@@ -263,16 +285,47 @@ export default defineComponent({
: this.store.stockList[this.store.chosenStockListIndex]; : this.store.stockList[this.store.chosenStockListIndex];
}, },
stockHasWarnings() { lengthExceeded() {
return ( return (
this.tooManyLocomotives || this.trainTooHeavy || this.trainTooLong || this.locoNotSuitable (this.store.totalLength > 350 && this.store.isTrainPassenger) ||
(this.store.totalLength > 650 && !this.store.isTrainPassenger)
); );
}, },
chosenRealComposition() { weightExceeded() {
const currentStockString = this.store.stockList.map((s) => s.type).join(';'); return this.store.acceptableWeight && this.store.totalWeight > this.store.acceptableWeight;
},
return this.store.realCompositionList.find((rc) => rc.stockString == currentStockString); locoNotSuitable() {
return (
!this.store.isTrainPassenger &&
this.store.stockList.length > 1 &&
!this.store.stockList.every((stock) => stock.isLoco) &&
this.store.stockList.some((stock) => stock.isLoco && stock.type.startsWith('EP'))
);
},
locoCountExceeded() {
return (
this.store.stockList.reduce((acc, stock) => {
if (stock.isLoco) acc += stock.count;
return acc;
}, 0) > 2
);
},
teamOnlyVehicles() {
return this.store.stockList.filter((stock) => stock.restrictions.teamOnly);
},
hasAnyWarnings() {
return (
this.locoCountExceeded ||
this.weightExceeded ||
this.lengthExceeded ||
this.locoNotSuitable ||
this.teamOnlyVehicles
);
}, },
}, },
@@ -431,7 +484,7 @@ export default defineComponent({
this.loadStockFromString(stockString); this.loadStockFromString(stockString);
}; };
reader.onerror = (err) => console.log(err); reader.onerror = (err) => console.error(err);
inputEl.value = ''; inputEl.value = '';
}, },
@@ -470,10 +523,12 @@ export default defineComponent({
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 0.5em; gap: 0.5em;
position: relative;
} }
.warning { .warning {
padding: 0.25em; padding: 0.25em;
margin: 0.25em 0;
background: $accentColor; background: $accentColor;
color: black; color: black;
@@ -536,10 +591,20 @@ export default defineComponent({
} }
} }
ul { .list-wrapper {
position: relative; position: relative;
overflow: auto; }
max-height: 500px;
.list-empty {
background-color: $secondaryColor;
border-radius: 0.5em;
padding: 0.75em;
font-weight: bold;
}
ul {
overflow-y: scroll;
height: 500px;
} }
ul > li { ul > li {
@@ -556,16 +621,11 @@ ul > li {
&:focus-visible { &:focus-visible {
outline: 1px solid white; outline: 1px solid white;
} }
&.list-empty {
background-color: $secondaryColor;
border-radius: 0.5em;
padding: 0.75em;
}
} }
li > .stock-info { li > .stock-info {
display: flex; display: flex;
gap: 0.25em;
color: white; color: white;
font-weight: 700; font-weight: 700;
@@ -574,46 +634,39 @@ li > .stock-info {
& > span { & > span {
padding: 0.5em; padding: 0.5em;
margin-right: 0.25em;
display: flex;
justify-content: center;
align-items: center;
} }
} }
.sponsor { .stock-info-no,
color: salmon; .stock-info-type {
}
.stock-info {
&__no,
&__type {
background-color: $secondaryColor; background-color: $secondaryColor;
&[data-team-only='true'] {
color: $teamColor;
} }
&__count { &[data-sponsor-only] {
background-color: #e04e3e; color: $sponsorColor;
} }
}
&__no { .stock-info-no {
min-width: 3.5em; min-width: 3.5em;
text-align: right; text-align: right;
&[data-selected='true'] { &[data-selected='true'] {
color: $accentColor; color: $accentColor;
} }
} }
&__cargo { .stock-info-cargo {
background-color: #333; background-color: #333;
} }
&__length, .stock-info-length,
&__mass, .stock-info-mass,
&__speed { .stock-info-speed {
background-color: #555; background-color: #555;
}
} }
.stock-list-anim { .stock-list-anim {
+75 -46
View File
@@ -45,43 +45,31 @@
<tbody> <tbody>
<tr <tr
v-for="{ vehicle, show } in computedTableData" v-for="{ vehicle, show } in computedTableData"
tabindex="0"
v-show="show" v-show="show"
tabindex="0"
:key="vehicle.type" :key="vehicle.type"
@click="previewVehicle(vehicle)" @click="previewVehicle(vehicle)"
@keydown.enter="previewVehicle(vehicle)" @keydown.enter="previewVehicle(vehicle)"
@dblclick="addVehicle(vehicle)" @dblclick="addVehicle(vehicle)"
ref="itemRefs"
> >
<td style="width: 120px"> <td style="width: 120px">
<img <img width="120" src="" :data-src="getThumbnailURL(vehicle.type, 'small')" />
width="120"
:src="getThumbnailURL(vehicle.type, 'small')"
:alt="`${vehicle.type}`"
loading="lazy"
@error="(e) => ((e.target as HTMLElement).style.display = 'none')"
/>
</td> </td>
<td :data-sponsoronly="vehicle.isSponsorsOnly"> <td
{{ vehicle.type }} :data-sponsor-only="vehicle.restrictions.sponsorOnly > 0"
:data-team-only="vehicle.restrictions.teamOnly"
style="min-width: 150px"
>
{{ vehicle.type.replace(/_/g, ' ') }}
</td> </td>
<td v-if="isLocomotive(vehicle)"> <td style="min-width: 100px">{{ $t(`wiki.${vehicle.group}`) }}</td>
{{ $t(`wiki.${vehicle.power}`) }}
</td>
<td v-else>{{ $t(`wiki.${vehicle.useType}`) }}</td>
<td>{{ vehicle.constructionType }}</td> <td>{{ vehicle.constructionType }}</td>
<td>{{ vehicle.length }}m</td> <td>{{ vehicle.length }}</td>
<td>{{ (vehicle.weight / 1000).toFixed(1) }}t</td> <td>{{ (vehicle.weight / 1000).toFixed(1) }}</td>
<td>{{ vehicle.maxSpeed }}km/h</td> <td>{{ vehicle.maxSpeed }}</td>
<td v-if="currentFilterMode == 'carriages'">
{{ !isLocomotive(vehicle) ? vehicle.cargoTypes.length : '---' }}
</td>
<td v-if="currentFilterMode == 'tractions'">
{{ isLocomotive(vehicle) ? (vehicle.coldStart ? `&check;` : '&cross;') : '---' }}
</td>
</tr> </tr>
</tbody> </tbody>
@@ -97,7 +85,7 @@ import { defineComponent } from 'vue';
import { useStore } from '../../store'; import { useStore } from '../../store';
import stockPreviewMixin from '../../mixins/stockPreviewMixin'; import stockPreviewMixin from '../../mixins/stockPreviewMixin';
import { IVehicle } from '../../types'; import { IVehicle } from '../../types';
import { isLocomotive } from '../../utils/vehicleUtils'; import { isTractionUnit } from '../../utils/vehicleUtils';
import stockMixin from '../../mixins/stockMixin'; import stockMixin from '../../mixins/stockMixin';
import imageMixin from '../../mixins/imageMixin'; import imageMixin from '../../mixins/imageMixin';
@@ -121,6 +109,7 @@ interface IWikiHeader {
interface IWikiRow { interface IWikiRow {
vehicle: IVehicle; vehicle: IVehicle;
show: boolean; show: boolean;
showImage: boolean;
} }
const headers: IWikiHeader[] = [ const headers: IWikiHeader[] = [
@@ -131,8 +120,8 @@ const headers: IWikiHeader[] = [
{ id: 'length', sortable: true, for: 'all' }, { id: 'length', sortable: true, for: 'all' },
{ id: 'weight', sortable: true, for: 'all' }, { id: 'weight', sortable: true, for: 'all' },
{ id: 'maxSpeed', sortable: true, for: 'all' }, { id: 'maxSpeed', sortable: true, for: 'all' },
{ id: 'coldStart', sortable: true, for: 'tractions' }, // { id: 'coldStart', sortable: true, for: 'tractions' },
{ id: 'cargoCount', sortable: true, for: 'carriages' }, // { id: 'cargoCount', sortable: true, for: 'carriages' },
]; ];
export default defineComponent({ export default defineComponent({
@@ -141,6 +130,7 @@ export default defineComponent({
data() { data() {
return { return {
store: useStore(), store: useStore(),
observer: null as IntersectionObserver | null,
headers, headers,
scrollTop: 0, scrollTop: 0,
@@ -156,6 +146,10 @@ export default defineComponent({
}; };
}, },
mounted() {
this.mountObserver();
},
activated() { activated() {
const tableWrapperRef = this.$refs['table-wrapper'] as HTMLElement; const tableWrapperRef = this.$refs['table-wrapper'] as HTMLElement;
@@ -165,10 +159,34 @@ export default defineComponent({
}, },
methods: { methods: {
isLocomotive, isTractionUnit,
mountObserver() {
if (this.observer) return;
this.observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.intersectionRatio > 0) {
entry.target
.querySelector('td:first-child > img')!
.setAttribute(
'src',
entry.target.querySelector('td:first-child > img')!.getAttribute('data-src')!
);
}
});
});
(this.$refs['itemRefs'] as HTMLElement[]).forEach((el) => this.observer?.observe(el));
},
toggleFilter(name: typeof this.currentFilterMode) { toggleFilter(name: typeof this.currentFilterMode) {
this.currentFilterMode = this.currentFilterMode == name ? 'all' : name; this.currentFilterMode = this.currentFilterMode == name ? 'all' : name;
const tableWrapperRef = this.$refs['table-wrapper'] as HTMLElement;
tableWrapperRef.scrollTo({
top: this.scrollTop,
});
}, },
toggleSorter(header: IWikiHeader) { toggleSorter(header: IWikiHeader) {
@@ -198,14 +216,16 @@ export default defineComponent({
case 'cargoCount': case 'cargoCount':
return ( return (
(!isLocomotive(row1.vehicle) ? Math.sign(row1.vehicle.cargoTypes.length || -1) : -1) - Math.sign(
(!isLocomotive(row2.vehicle) ? (row2.vehicle.cargoTypes.length || -1) * direction : -1) (!isTractionUnit(row1.vehicle) ? row1.vehicle.cargoTypes.length || -1 : -1) -
(!isTractionUnit(row2.vehicle) ? row2.vehicle.cargoTypes.length || -1 : -1)
) * direction
); );
case 'coldStart': case 'coldStart':
return ( return (
((isLocomotive(row1.vehicle) && row1.vehicle.coldStart ? 1 : -1) - ((isTractionUnit(row1.vehicle) && row1.vehicle.coldStart ? 1 : -1) -
(isLocomotive(row2.vehicle) && row2.vehicle.coldStart ? 1 : -1)) * (isTractionUnit(row2.vehicle) && row2.vehicle.coldStart ? 1 : -1)) *
direction direction
); );
@@ -224,11 +244,12 @@ export default defineComponent({
return this.store.vehicleDataList return this.store.vehicleDataList
.map((vehicle) => ({ .map((vehicle) => ({
vehicle, vehicle,
showImage: false,
show: show:
new RegExp(`${this.searchedVehicleTypeName.trim()}`, 'i').test(vehicle.type) && new RegExp(`${this.searchedVehicleTypeName.trim()}`, 'i').test(vehicle.type) &&
(this.currentFilterMode == 'all' || (this.currentFilterMode == 'all' ||
(this.currentFilterMode == 'tractions' && isLocomotive(vehicle)) || (this.currentFilterMode == 'tractions' && isTractionUnit(vehicle)) ||
(this.currentFilterMode == 'carriages' && !isLocomotive(vehicle))), (this.currentFilterMode == 'carriages' && !isTractionUnit(vehicle))),
})) }))
.sort((a, b) => this.sortTableRows(a, b)); .sort((a, b) => this.sortTableRows(a, b));
}, },
@@ -260,8 +281,6 @@ export default defineComponent({
flex-wrap: wrap; flex-wrap: wrap;
gap: 0.5em; gap: 0.5em;
margin: 0.5em 0;
} }
.actions-panel_vehicles { .actions-panel_vehicles {
@@ -275,10 +294,14 @@ export default defineComponent({
} }
} }
.tab_content {
display: grid;
grid-template-rows: 30px 770px;
gap: 0.5em;
}
.table-wrapper { .table-wrapper {
overflow: auto; overflow: auto;
height: 750px;
max-height: 95vh;
} }
.wiki-list table { .wiki-list table {
@@ -302,10 +325,6 @@ export default defineComponent({
cursor: pointer; cursor: pointer;
background-color: #333; background-color: #333;
&:first-child {
min-width: 120px;
}
&:nth-child(odd) { &:nth-child(odd) {
background-color: #444; background-color: #444;
} }
@@ -317,11 +336,21 @@ export default defineComponent({
td { td {
text-align: center; text-align: center;
height: 70px;
padding: 0.25em; padding: 0.25em;
height: 75px;
&[data-sponsoronly='true'] { min-width: 95px;
color: salmon;
&[data-sponsor-only='true'] {
color: $sponsorColor;
}
&[data-team-only='true'] {
color: $teamColor;
}
img {
vertical-align: middle;
} }
} }
} }
+2 -2
View File
@@ -5,7 +5,7 @@
v-for="(stock, stockIndex) in store.stockList" v-for="(stock, stockIndex) in store.stockList"
:key="stockIndex" :key="stockIndex"
:data-selected="store.chosenStockListIndex == stockIndex" :data-selected="store.chosenStockListIndex == stockIndex"
:data-sponsor="stock.isSponsorsOnly" :data-sponsor="stock.restrictions.sponsorOnly"
draggable="true" draggable="true"
@dragstart="onDragStart(stockIndex)" @dragstart="onDragStart(stockIndex)"
@drop="onDrop($event, stockIndex)" @drop="onDrop($event, stockIndex)"
@@ -43,7 +43,7 @@ const onListItemClick = (index: number) => {
}; };
const stockImageError = (e: Event, stock: IStock) => { const stockImageError = (e: Event, stock: IStock) => {
(e.target as HTMLImageElement).src = `images/${stock.useType}-unknown.png`; (e.target as HTMLImageElement).src = `images/${stock.group}-unknown.png`;
}; };
watch( watch(
-9
View File
@@ -1,9 +0,0 @@
export const enum EVehicleUseType {
LOCO_ELECTRICAL = 'loco-e',
LOCO_DIESEL = 'loco-s',
EMU = 'loco-ezt',
DMU = 'loco-szt',
CAR_PASSENGER = 'car-passenger',
CAR_CARGO = 'car-cargo',
}
+2 -2
View File
@@ -3,8 +3,8 @@ import axios from 'axios';
const http = axios.create({ const http = axios.create({
baseURL: baseURL:
import.meta.env.VITE_API_DEV === '1' && import.meta.env.DEV import.meta.env.VITE_API_DEV === '1' && import.meta.env.DEV
? 'http://localhost:5500' ? 'http://localhost:3001'
: 'https://static.spythere.eu', : 'https://stacjownik.spythere.eu',
}); });
export default http; export default http;
+22 -19
View File
@@ -15,12 +15,12 @@
"cargo-title": "Cargo (only selected freight cars)", "cargo-title": "Cargo (only selected freight cars)",
"no-cargo-available": "no cargo available", "no-cargo-available": "no cargo available",
"cargo-empty": "empty", "cargo-empty": "empty",
"loco-e": "ELECTR.", "loco-electric": "ELECTR.",
"loco-s": "DIESEL", "loco-diesel": "DIESEL",
"loco-ezt": "EMU", "unit-electric": "EMU",
"loco-szt": "DMU", "unit-diesel": "DMU",
"car-passenger": "PASSENGER", "wagon-passenger": "PASSENGER",
"car-cargo": "FREIGHT", "wagon-freight": "FREIGHT",
"action-add": "ADD NEW", "action-add": "ADD NEW",
"action-swap": "SWAP WITH", "action-swap": "SWAP WITH",
"real-stock": "POLISH TRAIN COMPOSITIONS" "real-stock": "POLISH TRAIN COMPOSITIONS"
@@ -30,12 +30,13 @@
"loading": "IMAGE LOADING...", "loading": "IMAGE LOADING...",
"desc": "Choose a railway vehicle above to see its preview", "desc": "Choose a railway vehicle above to see its preview",
"sponsor-only": "* SPONSORS ONLY UNTIL {0}", "sponsor-only": "* SPONSORS ONLY UNTIL {0}",
"loco-e": "ELECTRIC LOCO", "team-only": "* TD2 TEAM ONLY",
"loco-s": "DIESEL LOCO", "loco-electric": "ELECTRIC LOCO",
"loco-ezt": "ELECTRIC M.U.", "loco-diesel": "DIESEL LOCO",
"loco-szt": "DIESEL M.U.", "unit-electric": "ELECTRIC M.U.",
"car-passenger": "PASSENGER CARRIAGE", "unit-diesel": "DIESEL M.U.",
"car-cargo": "FREIGHT CARRIAGE", "wagon-passenger": "PASSENGER CARRIAGE",
"wagon-freight": "FREIGHT CARRIAGE",
"cabin": "Cabin type:", "cabin": "Cabin type:",
"construction": "Construction type:" "construction": "Construction type:"
}, },
@@ -68,11 +69,13 @@
"coldstart-info": "Locomotive cold start", "coldstart-info": "Locomotive cold start",
"doublemanning-info": "Double manning", "doublemanning-info": "Double manning",
"list-empty": "Stock list is empty!", "list-empty": "Stock list is empty!",
"warning-not-suitable": "EP07 & EP08 type locomotives are designed for passenger traffic only!",
"warning-not-suitable": "EP series locomotives are designed for passenger traffic only!",
"warning-passenger-too-long": "Maximum length of a passenger train may not be greater than 350m!", "warning-passenger-too-long": "Maximum length of a passenger train may not be greater than 350m!",
"warning-freight-too-long": "Maximum length of a freight train may not be greater than 650m!", "warning-freight-too-long": "Maximum length of a freight train may not be greater than 650m!",
"warning-too-many-locos": "This train has too many traction units!", "warning-too-many-locos": "This train has too many traction units!",
"warning-too-heavy": "This train is too heavy! Check {href}", "warning-too-heavy": "This train is too heavy! Check {href}",
"warning-team-only-vehicle": "There's at least one vehicle available only for TD2 team members in your stock composition! ({0})",
"acceptable-mass-docs": "acceptable rolling stock masses (PL)" "acceptable-mass-docs": "acceptable rolling stock masses (PL)"
}, },
"stockgen": { "stockgen": {
@@ -173,12 +176,12 @@
"maxSpeed": "Speed", "maxSpeed": "Speed",
"cargoCount": "Cargo count" "cargoCount": "Cargo count"
}, },
"loco-ezt": "EMU", "unit-electric": "EMU",
"loco-szt": "DMU", "unit-diesel": "DMU",
"loco-s": "Diesel locomotive", "loco-diesel": "Diesel locomotive",
"loco-e": "Electric locomotive", "loco-electric": "Electric locomotive",
"car-passenger": "Passenger carriage", "wagon-passenger": "Passenger carriage",
"car-cargo": "Frieght carriage" "wagon-freight": "Frieght carriage"
}, },
"realstock": { "realstock": {
"title": "POLISH TRAIN COMPOSITIONS by", "title": "POLISH TRAIN COMPOSITIONS by",
+23 -20
View File
@@ -9,18 +9,18 @@
"version-check": "Strona jest kompletna dla wersji {version} symulatora TD2" "version-check": "Strona jest kompletna dla wersji {version} symulatora TD2"
}, },
"inputs": { "inputs": {
"title": "WYBIERZ POJAZD SZYNOWY", "title": "WYBIERZ POJAZD",
"input-vehicle": "Wybierz pojazd trakcyjny", "input-vehicle": "Wybierz pojazd trakcyjny",
"input-carwagon": "Wybierz wagon", "input-carwagon": "Wybierz wagon",
"cargo-title": "Ładunek (tylko wybrane towarowe)", "cargo-title": "Ładunek (tylko wybrane towarowe)",
"no-cargo-available": "brak dostępnych ładunków", "no-cargo-available": "brak dostępnych ładunków",
"cargo-empty": "próżny", "cargo-empty": "próżny",
"loco-e": "ELEKTR.", "loco-electric": "ELEKTR.",
"loco-s": "SPAL.", "loco-diesel": "SPAL.",
"loco-ezt": "EZT", "unit-electric": "EZT",
"loco-szt": "SZT", "unit-diesel": "SZT",
"car-passenger": "PASAŻERSKIE", "wagon-passenger": "PASAŻERSKIE",
"car-cargo": "TOWAROWE", "wagon-freight": "TOWAROWE",
"action-add": "DODAJ NOWY", "action-add": "DODAJ NOWY",
"action-swap": "ZAMIEŃ ZA", "action-swap": "ZAMIEŃ ZA",
"real-stock": "REALNE ZESTAWIENIA" "real-stock": "REALNE ZESTAWIENIA"
@@ -30,12 +30,13 @@
"loading": "ŁADOWANIE OBRAZU...", "loading": "ŁADOWANIE OBRAZU...",
"desc": "Wybierz pojazd lub wagon, aby zobaczyć jego podgląd powyżej", "desc": "Wybierz pojazd lub wagon, aby zobaczyć jego podgląd powyżej",
"sponsor-only": "* TYLKO DLA SPONSORÓW DO {0}", "sponsor-only": "* TYLKO DLA SPONSORÓW DO {0}",
"loco-e": "ELEKTROWÓZ", "team-only": "* TYLKO DLA ZESPOŁU TD2",
"loco-s": "SPALINOWÓZ", "loco-electric": "ELEKTROWÓZ",
"loco-ezt": "EZT", "loco-diesel": "SPALINOWÓZ",
"loco-szt": "SZT", "unit-electric": "EZT",
"car-passenger": "WAGON PASAŻERSKI", "unit-diesel": "SZT",
"car-cargo": "WAGON TOWAROWY", "wagon-passenger": "WAGON PASAŻERSKI",
"wagon-freight": "WAGON TOWAROWY",
"cabin": "Typ kabiny:", "cabin": "Typ kabiny:",
"construction": "Typ konstrukcji:" "construction": "Typ konstrukcji:"
}, },
@@ -68,11 +69,13 @@
"coldstart-info": "Zimny start", "coldstart-info": "Zimny start",
"doublemanning-info": "Podwójna obsada", "doublemanning-info": "Podwójna obsada",
"list-empty": "Lista pojazdów jest pusta!", "list-empty": "Lista pojazdów jest pusta!",
"warning-not-suitable": "Lokomotywy EP07 i EP08 są przeznaczone jedynie do ruchu pasażerskiego!",
"warning-not-suitable": "Lokomotywy serii EP są przeznaczone jedynie do ruchu pasażerskiego!",
"warning-passenger-too-long": "Maksymalna długość składów pasażerskich nie może przekraczać 350m!", "warning-passenger-too-long": "Maksymalna długość składów pasażerskich nie może przekraczać 350m!",
"warning-freight-too-long": "Maksymalna długość składów innych niż pasażerskie nie może przekraczać 650m!", "warning-freight-too-long": "Maksymalna długość składów innych niż pasażerskie nie może przekraczać 650m!",
"warning-too-many-locos": "Ten skład posiada za dużo pojazdów trakcyjnych!", "warning-too-many-locos": "Ten skład posiada za dużo pojazdów trakcyjnych!",
"warning-too-heavy": "Ten skład jest za ciężki! Sprawdź {href}", "warning-too-heavy": "Ten skład jest za ciężki! Sprawdź {href}",
"warning-team-only-vehicle": "W zestawieniu znajduje się co najmniej jeden pojazd dostępny tylko dla członków zespołu TD2! ({0})",
"acceptable-mass-docs": "dopuszczalne masy składów" "acceptable-mass-docs": "dopuszczalne masy składów"
}, },
"stockgen": { "stockgen": {
@@ -173,12 +176,12 @@
"maxSpeed": "Prędkość", "maxSpeed": "Prędkość",
"cargoCount": "Ładunki" "cargoCount": "Ładunki"
}, },
"loco-ezt": "EZT", "loco-diesel": "Spalinowóz",
"loco-szt": "SZT", "loco-electric": "Elektrowóz",
"loco-s": "Spalinowóz", "unit-electric": "EZT",
"loco-e": "Elektrowóz", "unit-diesel": "SZT",
"car-passenger": "Wagon pasażerski", "wagon-passenger": "Wagon pasażerski",
"car-cargo": "Wagon towarowy" "wagon-freight": "Wagon towarowy"
}, },
"realstock": { "realstock": {
"title": "ZESTAWIENIA REALNE by", "title": "ZESTAWIENIA REALNE by",
+1 -5
View File
@@ -7,11 +7,7 @@ export default defineComponent({
}, },
getThumbnailURL(vehicleType: string, size: 'small' | 'large') { getThumbnailURL(vehicleType: string, size: 'small' | 'large') {
return `${ return `https://static.spythere.eu/images/${vehicleType}--${size == 'small' ? 300 : 800}px.jpg`;
import.meta.env.VITE_API_DEV === '1'
? 'http://localhost:5500'
: 'https://static.spythere.eu'
}/images/${vehicleType}--${size == 'small' ? 300 : 800}px.jpg`;
}, },
}, },
}); });
+5 -7
View File
@@ -1,7 +1,7 @@
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { useStore } from '../store'; import { useStore } from '../store';
import { ICarWagon, ILocomotive, IStock, ICargo, IVehicle } from '../types'; import { ICarWagon, ILocomotive, IStock, ICargo, IVehicle } from '../types';
import { isLocomotive } from '../utils/vehicleUtils'; import { isTractionUnit } from '../utils/vehicleUtils';
export default defineComponent({ export default defineComponent({
setup() { setup() {
@@ -16,7 +16,7 @@ export default defineComponent({
}, },
getStockObject(vehicle: IVehicle, cargo?: ICargo | null, count = 1): IStock { getStockObject(vehicle: IVehicle, cargo?: ICargo | null, count = 1): IStock {
const isLoco = isLocomotive(vehicle); const isLoco = isTractionUnit(vehicle);
return { return {
id: this.getStockId(), id: this.getStockId(),
@@ -27,11 +27,9 @@ export default defineComponent({
isLoco, isLoco,
cargo: !isLoco && vehicle.loadable && cargo ? cargo : undefined, cargo: !isLoco && vehicle.loadable && cargo ? cargo : undefined,
count, count,
imgSrc: vehicle.imageSrc, group: isLoco ? vehicle.group : vehicle.group,
useType: isLoco ? vehicle.power : vehicle.useType,
isSponsorsOnly: vehicle.isSponsorsOnly,
constructionType: vehicle.constructionType, constructionType: vehicle.constructionType,
sponsorsOnlyTimestamp: vehicle.sponsorsOnlyTimestamp, restrictions: vehicle.restrictions,
}; };
}, },
@@ -92,7 +90,7 @@ export default defineComponent({
if (cargo) vehicleCargo = vehicle?.cargoTypes.find((c) => c.id == cargo) || null; if (cargo) vehicleCargo = vehicle?.cargoTypes.find((c) => c.id == cargo) || null;
} }
if (!vehicle) console.log('Brak pojazdu / rodzaj pojazdu źle wczytany:', type); if (!vehicle) console.warn('Brak pojazdu / rodzaj pojazdu źle wczytany:', type);
this.addVehicle(vehicle, vehicleCargo); this.addVehicle(vehicle, vehicleCargo);
}); });
+8 -8
View File
@@ -1,7 +1,7 @@
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { useStore } from '../store'; import { useStore } from '../store';
import { ICarWagon, ILocomotive, IStock, IVehicle } from '../types'; import { ICarWagon, ILocomotive, IStock, IVehicle, LocoGroupType, WagonGroupType } from '../types';
import { isLocomotive } from '../utils/vehicleUtils'; import { isTractionUnit } from '../utils/vehicleUtils';
export default defineComponent({ export default defineComponent({
setup() { setup() {
@@ -14,40 +14,40 @@ export default defineComponent({
methods: { methods: {
previewStock(stock: IStock) { previewStock(stock: IStock) {
if (this.store.chosenVehicle?.imageSrc != stock.imgSrc) this.store.imageLoading = true; // if (this.store.chosenVehicle?.imageSrc != stock.imgSrc) this.store.imageLoading = true;
if (stock.isLoco) { if (stock.isLoco) {
const chosenLoco = this.store.locoDataList.find((v) => v.type == stock.type) || null; const chosenLoco = this.store.locoDataList.find((v) => v.type == stock.type) || null;
this.store.chosenVehicle = chosenLoco; this.store.chosenVehicle = chosenLoco;
this.store.chosenLoco = chosenLoco; this.store.chosenLoco = chosenLoco;
this.store.chosenCargo = null; this.store.chosenCargo = null;
this.store.chosenLocoPower = stock.useType; this.store.chosenLocoGroup = stock.group as LocoGroupType;
} else { } else {
const chosenCar = this.store.carDataList.find((v) => v.type == stock.type) || null; const chosenCar = this.store.carDataList.find((v) => v.type == stock.type) || null;
this.store.chosenVehicle = chosenCar; this.store.chosenVehicle = chosenCar;
this.store.chosenCar = chosenCar; this.store.chosenCar = chosenCar;
this.store.chosenCargo = stock.cargo || null; this.store.chosenCargo = stock.cargo || null;
this.store.chosenCarUseType = stock.useType; this.store.chosenCarGroup = stock.group as WagonGroupType;
} }
}, },
previewLocomotive(loco: ILocomotive) { previewLocomotive(loco: ILocomotive) {
this.store.chosenLoco = loco; this.store.chosenLoco = loco;
this.store.chosenVehicle = loco; this.store.chosenVehicle = loco;
this.store.chosenLocoPower = loco.power; this.store.chosenLocoGroup = loco.group;
}, },
previewCarWagon(carWagon: ICarWagon) { previewCarWagon(carWagon: ICarWagon) {
this.store.chosenCar = carWagon; this.store.chosenCar = carWagon;
this.store.chosenCarUseType = carWagon.useType; this.store.chosenCarGroup = carWagon.group;
this.store.chosenVehicle = carWagon; this.store.chosenVehicle = carWagon;
this.store.chosenCargo = null; this.store.chosenCargo = null;
}, },
previewVehicle(vehicle: IVehicle) { previewVehicle(vehicle: IVehicle) {
if (isLocomotive(vehicle)) this.previewLocomotive(vehicle); if (isTractionUnit(vehicle)) this.previewLocomotive(vehicle);
else this.previewCarWagon(vehicle); else this.previewCarWagon(vehicle);
}, },
-42
View File
@@ -1,42 +0,0 @@
import { defineComponent } from 'vue';
import { useStore } from '../store';
export default defineComponent({
setup() {
const store = useStore();
return {
store,
};
},
computed: {
trainTooLong() {
return (
(this.store.totalLength > 350 && this.store.isTrainPassenger) ||
(this.store.totalLength > 650 && !this.store.isTrainPassenger)
);
},
trainTooHeavy() {
return this.store.acceptableWeight && this.store.totalWeight > this.store.acceptableWeight;
},
locoNotSuitable() {
return (
!this.store.isTrainPassenger &&
this.store.stockList.length > 1 &&
!this.store.stockList.every((stock) => stock.isLoco) &&
this.store.stockList.some((stock) => stock.isLoco && stock.type.startsWith('EP'))
);
},
tooManyLocomotives() {
return (
this.store.stockList.reduce((acc, stock) => {
if (stock.isLoco) acc += stock.count;
return acc;
}, 0) > 2
);
},
},
});
+25 -23
View File
@@ -1,11 +1,13 @@
import { import {
IVehiclesAPI, IVehiclesData,
ICarWagon, ICarWagon,
ILocomotive, ILocomotive,
ICargo, ICargo,
IVehicle, IVehicle,
IStock, IStock,
IRealComposition, IRealComposition,
LocoGroupType,
WagonGroupType,
} from './types'; } from './types';
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import { import {
@@ -17,8 +19,9 @@ import {
totalLength, totalLength,
totalWeight, totalWeight,
} from './utils/vehicleUtils'; } from './utils/vehicleUtils';
import http from './http';
import i18n from './i18n-setup'; import i18n from './i18n-setup';
import http from './http';
export const useStore = defineStore({ export const useStore = defineStore({
id: 'store', id: 'store',
@@ -33,8 +36,8 @@ export const useStore = defineStore({
imageLoading: false, imageLoading: false,
chosenLocoPower: 'loco-e', chosenLocoGroup: 'loco-electric' as LocoGroupType,
chosenCarUseType: 'car-passenger', chosenCarGroup: 'wagon-passenger' as WagonGroupType,
stockList: [] as IStock[], stockList: [] as IStock[],
cargoOptions: [] as any[][], cargoOptions: [] as any[][],
@@ -50,17 +53,17 @@ export const useStore = defineStore({
isRandomizerCardOpen: false, isRandomizerCardOpen: false,
isRealStockListCardOpen: false, isRealStockListCardOpen: false,
vehiclesAPIData: undefined as IVehiclesAPI | undefined, vehiclesData: undefined as IVehiclesData | undefined,
lastFocusedElement: null as HTMLElement | null, lastFocusedElement: null as HTMLElement | null,
}), }),
getters: { getters: {
locoDataList: (state) => locoDataList(state.vehiclesAPIData), locoDataList: (state) => locoDataList(state.vehiclesData),
carDataList: (state) => carDataList(state.vehiclesAPIData), carDataList: (state) => carDataList(state.vehiclesData),
vehicleDataList: (state) => [ vehicleDataList: (state) => [
...locoDataList(state.vehiclesAPIData), ...locoDataList(state.vehiclesData),
...carDataList(state.vehiclesAPIData), ...carDataList(state.vehiclesData),
], ],
totalWeight: (state) => totalWeight(state.stockList), totalWeight: (state) => totalWeight(state.stockList),
totalLength: (state) => totalLength(state.stockList), totalLength: (state) => totalLength(state.stockList),
@@ -69,16 +72,16 @@ export const useStore = defineStore({
acceptableWeight: (state) => acceptableWeight(state.stockList), acceptableWeight: (state) => acceptableWeight(state.stockList),
realCompositionList: (state) => { realCompositionList: (state) => {
if (!state.vehiclesAPIData) return []; if (!state.vehiclesData) return [];
return Object.keys(state.vehiclesAPIData.realCompositions).reduce<IRealComposition[]>( return Object.keys(state.vehiclesData.realCompositions).reduce<IRealComposition[]>(
(acc, key) => { (acc, key) => {
const [type, number, ...name] = key.split(' '); const [type, number, ...name] = key.split(' ');
const obj = { const obj = {
number: number.replace(/_/g, '/'), number: number.replace(/_/g, '/'),
name: name.join(' '), name: name.join(' '),
stockString: state.vehiclesAPIData!.realCompositions[key], stockString: state.vehiclesData!.realCompositions[key],
type, type,
}; };
@@ -100,9 +103,8 @@ export const useStore = defineStore({
const headingLoco = state.stockList[0]; const headingLoco = state.stockList[0];
return ( return (
state.vehiclesAPIData?.vehicleProps.find( state.vehiclesData?.vehicleProps.find((stock) => stock.type == headingLoco.constructionType)
(stock) => stock.type == headingLoco.constructionType ?.coldStart ?? false
)?.coldStart ?? false
); );
}, },
@@ -113,9 +115,8 @@ export const useStore = defineStore({
const headingLoco = state.stockList[0]; const headingLoco = state.stockList[0];
return ( return (
state.vehiclesAPIData?.vehicleProps.find( state.vehiclesData?.vehicleProps.find((stock) => stock.type == headingLoco.constructionType)
(stock) => stock.type == headingLoco.constructionType ?.doubleManned ?? false
)?.doubleManned ?? false
); );
}, },
}, },
@@ -123,8 +124,9 @@ export const useStore = defineStore({
actions: { actions: {
async fetchVehiclesAPI() { async fetchVehiclesAPI() {
try { try {
const vehiclesData = (await http.get<IVehiclesAPI>('/vehicles.json')).data; const vehiclesData = (await http.get<IVehiclesData>('/vehicles')).data;
this.vehiclesAPIData = vehiclesData; this.vehiclesData = vehiclesData;
} catch (error) { } catch (error) {
console.error(error); console.error(error);
} }
@@ -136,10 +138,10 @@ export const useStore = defineStore({
}, },
async mergeBackendTranslations() { async mergeBackendTranslations() {
if (!this.vehiclesAPIData) return; if (!this.vehiclesData) return;
i18n.global.mergeLocaleMessage('pl', this.vehiclesAPIData.vehicleLocales.pl); i18n.global.mergeLocaleMessage('pl', this.vehiclesData.vehicleLocales.pl);
i18n.global.mergeLocaleMessage('en', this.vehiclesAPIData.vehicleLocales.en); i18n.global.mergeLocaleMessage('en', this.vehiclesData.vehicleLocales.en);
}, },
handleRouting() { handleRouting() {
+3
View File
@@ -6,6 +6,9 @@ $textColor: #fff;
$secondaryColor: #1b1b1b; $secondaryColor: #1b1b1b;
$accentColor: #e4c428; $accentColor: #e4c428;
$sponsorColor: salmon;
$teamColor: gold;
@font-face { @font-face {
font-family: 'Lato'; font-family: 'Lato';
src: src:
+16 -31
View File
@@ -1,16 +1,17 @@
export type IVehicle = ILocomotive | ICarWagon; export type IVehicle = ILocomotive | ICarWagon;
export type StockSectionMode = 'STOCK_LIST' | 'STOCK_GENERATOR'; export type StockSectionMode = 'STOCK_LIST' | 'STOCK_GENERATOR';
export type TLocoGroup = 'loco-e' | 'loco-s' | 'loco-ezt' | 'loco-szt'; export type LocoGroupType = 'loco-electric' | 'loco-diesel' | 'unit-electric' | 'unit-diesel';
export type TCarWagonGroup = 'car-passenger' | 'car-cargo'; export type WagonGroupType = 'wagon-passenger' | 'wagon-freight';
export type VehicleGroupType = LocoGroupType | WagonGroupType;
export type RestrictionType = 'sponsorOnly' | 'teamOnly';
export interface IStockProps { export interface IVehicleProps {
type: string; type: string;
speed: number;
length: number; length: number;
// mass: number;
weight: number; weight: number;
// cargo?: string | null; cargoTypes?: ICargo[];
cargoTypes: ICargo[] | null;
coldStart?: boolean; coldStart?: boolean;
doubleManned?: boolean; doubleManned?: boolean;
} }
@@ -20,7 +21,7 @@ export interface ICargo {
weight: number; weight: number;
} }
export interface IVehiclesAPI { export interface IVehiclesData {
simulatorVersion: string; simulatorVersion: string;
generator: { generator: {
@@ -29,16 +30,9 @@ export interface IVehiclesAPI {
}; };
}; };
vehicleInfo: { vehicleList: any[][];
'car-cargo': [string, string, boolean, number | null, string][];
'car-passenger': [string, string, boolean, number | null, string][];
'loco-e': [string, string, string, string, number | null][];
'loco-s': [string, string, string, string, number | null][];
'loco-szt': [string, string, string, string, number | null][];
'loco-ezt': [string, string, string, string, number | null][];
};
vehicleProps: IStockProps[]; vehicleProps: IVehicleProps[];
vehicleLocales: { vehicleLocales: {
pl: { pl: {
@@ -56,14 +50,11 @@ export interface IVehiclesAPI {
export interface ILocomotive { export interface ILocomotive {
type: string; type: string;
power: TLocoGroup; group: LocoGroupType;
group: TLocoGroup;
constructionType: string; constructionType: string;
cabinType: string; cabinType: string;
maxSpeed: number; maxSpeed: number;
isSponsorsOnly: boolean; restrictions: Record<RestrictionType, any>;
sponsorsOnlyTimestamp: number;
imageSrc: string;
weight: number; weight: number;
length: number; length: number;
coldStart: boolean; coldStart: boolean;
@@ -72,14 +63,11 @@ export interface ILocomotive {
export interface ICarWagon { export interface ICarWagon {
type: string; type: string;
useType: TCarWagonGroup; group: WagonGroupType;
group: TCarWagonGroup;
constructionType: string; constructionType: string;
loadable: boolean; loadable: boolean;
isSponsorsOnly: boolean; restrictions: Record<RestrictionType, any>;
sponsorsOnlyTimestamp: number;
maxSpeed: number; maxSpeed: number;
imageSrc: string;
weight: number; weight: number;
length: number; length: number;
cargoTypes: ICargo[]; cargoTypes: ICargo[];
@@ -88,18 +76,15 @@ export interface ICarWagon {
export interface IStock { export interface IStock {
id: string; id: string;
type: string; type: string;
useType: string; group: LocoGroupType | WagonGroupType;
constructionType: string; constructionType: string;
length: number; length: number;
// mass: number;
weight: number; weight: number;
maxSpeed: number; maxSpeed: number;
cargo?: ICargo; cargo?: ICargo;
isLoco: boolean; isLoco: boolean;
isSponsorsOnly: boolean; restrictions: Record<RestrictionType, any>;
sponsorsOnlyTimestamp: number;
count: number; count: number;
imgSrc?: string;
} }
export interface IRealComposition { export interface IRealComposition {
+50 -53
View File
@@ -1,5 +1,11 @@
import { EVehicleUseType } from '../enums/EVehicleUseType'; import {
import { ICarWagon, ILocomotive, IStock, IVehiclesAPI, TCarWagonGroup, TLocoGroup } from '../types'; ICarWagon,
ILocomotive,
IStock,
IVehiclesData,
LocoGroupType,
WagonGroupType,
} from '../types';
import { import {
MassLimitLocoType, MassLimitLocoType,
SpeedLimitLocoType, SpeedLimitLocoType,
@@ -7,86 +13,77 @@ import {
calculateSpeedLimit, calculateSpeedLimit,
} from './vehicleLimitsUtils'; } from './vehicleLimitsUtils';
export function isLocomotive(vehicle: ILocomotive | ICarWagon): vehicle is ILocomotive { export function isTractionUnit(vehicle: ILocomotive | ICarWagon): vehicle is ILocomotive {
return (vehicle as ILocomotive).power !== undefined; return (vehicle as ILocomotive).cabinType !== undefined;
} }
export function locoDataList(vehiclesData: IVehiclesAPI | undefined) { export function locoDataList(vehiclesData: IVehiclesData | undefined) {
if (!vehiclesData) return []; if (!vehiclesData) return [];
return Object.keys(vehiclesData.vehicleInfo).reduce((acc, vehiclePower) => { return vehiclesData.vehicleList.reduce<ILocomotive[]>((acc, vehicleInfoArray) => {
if (!vehiclePower.startsWith('loco')) return acc; // check if data array has 5 elements (locos & units only)
if (vehicleInfoArray.length != 5) return acc;
const locoVehiclesData = vehiclesData.vehicleInfo[vehiclePower as TLocoGroup]; const [type, constructionType, cabinType, group, restrictions] = vehicleInfoArray;
locoVehiclesData.forEach((loco) => {
// if (!loco[4]) return;
const [type, constructionType, cabinType, maxSpeed, sponsorsTimestamp] = loco;
const locoProps = vehiclesData.vehicleProps.find((prop) => constructionType == prop.type); const locoProps = vehiclesData.vehicleProps.find((prop) => constructionType == prop.type);
if (!locoProps) {
console.warn('Brak atrybutów dla pojazdu:', type);
return acc;
}
acc.push({ acc.push({
power: vehiclePower as TLocoGroup, group: group as LocoGroupType,
group: vehiclePower as TLocoGroup,
type, type,
constructionType, constructionType,
cabinType, cabinType,
maxSpeed: Number(maxSpeed),
isSponsorsOnly: Number(sponsorsTimestamp) > Date.now(),
sponsorsOnlyTimestamp: Number(sponsorsTimestamp),
imageSrc: '',
length: restrictions: restrictions ?? {},
locoProps?.length && type.startsWith('2EN')
? locoProps.length * 2
: locoProps?.length ?? 0,
weight: locoProps?.weight && type.startsWith('2EN') ? 253000 : locoProps?.weight ?? 0,
coldStart: locoProps?.coldStart ?? false, maxSpeed: locoProps.speed,
doubleManned: locoProps?.doubleManned ?? false, length: locoProps.length,
}); weight: locoProps.weight,
coldStart: locoProps.coldStart ?? false,
doubleManned: locoProps.doubleManned ?? false,
}); });
return acc; return acc;
}, [] as ILocomotive[]); }, []);
} }
export function carDataList(vehiclesData: IVehiclesAPI | undefined) { export function carDataList(vehiclesData: IVehiclesData | undefined) {
if (!vehiclesData) return []; if (!vehiclesData) return [];
return Object.keys(vehiclesData.vehicleInfo).reduce((acc, vehicleUseType) => { return vehiclesData.vehicleList.reduce<ICarWagon[]>((acc, vehicleInfoArray) => {
if (!vehicleUseType.startsWith('car')) return acc; // check if data array has 4 elements (wagons only)
if (vehicleInfoArray.length != 4) return acc;
const carVehiclesData = vehiclesData.vehicleInfo[vehicleUseType as TCarWagonGroup]; const [type, constructionType, group, restrictions] = vehicleInfoArray;
const wagonProps = vehiclesData.vehicleProps.find((prop) => constructionType == prop.type);
carVehiclesData.forEach((car) => { if (!wagonProps) {
const [type, constructionType, loadable, sponsorsOnlyTimestamp, maxSpeed] = car; console.warn('Brak atrybutów dla pojazdu:', type);
return acc;
if (sponsorsOnlyTimestamp && Number(sponsorsOnlyTimestamp) <= Date.now()) return; }
const carPropsData = vehiclesData.vehicleProps.find((v) =>
type.toString().startsWith(v.type)
);
acc.push({ acc.push({
useType: vehicleUseType as TCarWagonGroup, group: group as WagonGroupType,
group: vehicleUseType as TCarWagonGroup,
type, type,
constructionType, constructionType,
loadable, loadable: wagonProps.cargoTypes ? wagonProps.cargoTypes.length > 0 : false,
isSponsorsOnly: Number(sponsorsOnlyTimestamp) > Date.now(), cargoTypes: wagonProps?.cargoTypes ?? [],
sponsorsOnlyTimestamp: Number(sponsorsOnlyTimestamp),
maxSpeed: Number(maxSpeed),
imageSrc: '',
cargoTypes: carPropsData?.cargoTypes ?? [],
weight: carPropsData?.weight || 0, restrictions: restrictions ?? {},
length: carPropsData?.length || 0,
}); maxSpeed: wagonProps.speed,
weight: wagonProps?.weight || 0,
length: wagonProps?.length || 0,
}); });
return acc; return acc;
}, [] as ICarWagon[]); }, []);
} }
export function totalWeight(stockList: IStock[]) { export function totalWeight(stockList: IStock[]) {
@@ -142,5 +139,5 @@ export function isTrainPassenger(stockList: IStock[]) {
return stockList return stockList
.filter((stock) => !stock.isLoco) .filter((stock) => !stock.isLoco)
.every((stock) => stock.useType === EVehicleUseType.CAR_PASSENGER); .every((stock) => stock.group === 'wagon-passenger');
} }
+14 -2
View File
@@ -13,6 +13,8 @@ export default defineConfig({
VitePWA({ VitePWA({
registerType: 'autoUpdate', registerType: 'autoUpdate',
includeAssets: ['/images/*.{png,svg,jpg}', '/fonts/*.{woff,woff2,ttf}'],
devOptions: { devOptions: {
suppressWarnings: true, suppressWarnings: true,
enabled: true, enabled: true,
@@ -23,7 +25,7 @@ export default defineConfig({
runtimeCaching: [ runtimeCaching: [
{ {
urlPattern: /^https:\/\/rj.td2.info.pl\/dist\/img\/thumbnails\/.*/i, urlPattern: new RegExp('^https://rj.td2.info.pl/dist/img/thumbnails/*', 'i'),
handler: 'CacheFirst', handler: 'CacheFirst',
options: { options: {
cacheName: 'swdr-images-cache', cacheName: 'swdr-images-cache',
@@ -37,7 +39,7 @@ export default defineConfig({
}, },
}, },
{ {
urlPattern: /^https:\/\/static.spythere.eu\/.*/i, urlPattern: new RegExp('^https://static.spythere.eu/images/*', 'i'),
handler: 'CacheFirst', handler: 'CacheFirst',
options: { options: {
cacheName: 'spythere-api-cache', cacheName: 'spythere-api-cache',
@@ -50,6 +52,16 @@ export default defineConfig({
}, },
}, },
}, },
{
urlPattern: new RegExp('^https://stacjownik.spythere.eu/vehicles', 'i'),
handler: 'NetworkFirst',
options: {
cacheName: 'vehicles-cache',
cacheableResponse: {
statuses: [200],
},
},
},
], ],
}, },
}), }),