Compare commits

...

18 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
Spythere 421ae1db6b Merge pull request #23 from Spythere/development
Wersja 1.8.3.1
2024-04-03 21:53:48 +02:00
Spythere 90824dc0e5 hotfix 2024-04-03 21:51:24 +02:00
32 changed files with 517 additions and 481 deletions
+2 -2
View File
@@ -1,7 +1,7 @@
{
"hosting": {
"public": "dist",
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
"ignore": [],
"rewrites": [
{
"source": "**",
@@ -9,4 +9,4 @@
}
]
}
}
}
+20 -24
View File
@@ -1,18 +1,18 @@
{
"name": "pojazdownik",
"version": "1.7.4",
"version": "1.8.4",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "pojazdownik",
"version": "1.7.4",
"version": "1.8.4",
"dependencies": {
"axios": "^1.4.0",
"pinia": "^2.0.17",
"prettier": "^3.0.3",
"vue": "^3.2.37",
"vue-i18n": "9"
"vue-i18n": "9.11.0"
},
"devDependencies": {
"@rushstack/eslint-patch": "^1.3.3",
@@ -2028,13 +2028,12 @@
"license": "BSD-3-Clause"
},
"node_modules/@intlify/core-base": {
"version": "9.9.1",
"resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.9.1.tgz",
"integrity": "sha512-qsV15dg7jNX2faBRyKMgZS8UcFJViWEUPLdzZ9UR0kQZpFVeIpc0AG7ZOfeP7pX2T9SQ5jSiorq/tii9nkkafA==",
"license": "MIT",
"version": "9.11.0",
"resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-9.11.0.tgz",
"integrity": "sha512-cveOqAstjLZIiyatcP/HrzrQ87cZI8ScPQna3yvoM8zjcjcIRK1MRvmxUNlPdg0rTNJMZw7rixPVM58O5aHVPA==",
"dependencies": {
"@intlify/message-compiler": "9.9.1",
"@intlify/shared": "9.9.1"
"@intlify/message-compiler": "9.11.0",
"@intlify/shared": "9.11.0"
},
"engines": {
"node": ">= 16"
@@ -2044,12 +2043,11 @@
}
},
"node_modules/@intlify/message-compiler": {
"version": "9.9.1",
"resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.9.1.tgz",
"integrity": "sha512-zTvP6X6HeumHOXuAE1CMMsV6tTX+opKMOxO1OHTCg5N5Sm/F7d8o2jdT6W6L5oHUsJ/vvkGefHIs7Q3hfowmsA==",
"license": "MIT",
"version": "9.11.0",
"resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-9.11.0.tgz",
"integrity": "sha512-x31Gl7cscnoI4UUY1yaIy8e7vVMVW1VVlTXZz4SIHKqoSEUkfmgqK8NAx1e7RcoHEbICR7uyCbud0ZL1s4OGXQ==",
"dependencies": {
"@intlify/shared": "9.9.1",
"@intlify/shared": "9.11.0",
"source-map-js": "^1.0.2"
},
"engines": {
@@ -2060,10 +2058,9 @@
}
},
"node_modules/@intlify/shared": {
"version": "9.9.1",
"resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.9.1.tgz",
"integrity": "sha512-b3Pta1nwkz5rGq434v0psHwEwHGy1pYCttfcM22IE//K9owbpkEvFptx9VcuRAxjQdrO2If249cmDDjBu5wMDA==",
"license": "MIT",
"version": "9.11.0",
"resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-9.11.0.tgz",
"integrity": "sha512-KHSNgi7sRjmSm7aD8QH8WFt9VfKaekJuJ473opbJlkGY3EDnDUU8ikIhG8PbasQbgNvbY3m3tWNGqk2omIdwMA==",
"engines": {
"node": ">= 16"
},
@@ -7624,13 +7621,12 @@
}
},
"node_modules/vue-i18n": {
"version": "9.9.1",
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.9.1.tgz",
"integrity": "sha512-xyQ4VspLdNSPTKBFBPWa1tvtj+9HuockZwgFeD2OhxxXuC2CWeNvV4seu2o9+vbQOyQbhAM5Ez56oxUrrnTWdw==",
"license": "MIT",
"version": "9.11.0",
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.11.0.tgz",
"integrity": "sha512-vU4gY6lu8Pdfs9BgKGiDAJmFDf88cceR47KcSB0VW4xJzUrXR/7qwqM7A8dQ2nedhoIDxoOm5Ro4pFd2KvJqbA==",
"dependencies": {
"@intlify/core-base": "9.9.1",
"@intlify/shared": "9.9.1",
"@intlify/core-base": "9.11.0",
"@intlify/shared": "9.11.0",
"@vue/devtools-api": "^6.5.0"
},
"engines": {
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "pojazdownik",
"version": "1.8.3",
"version": "1.8.4",
"private": true,
"type": "module",
"scripts": {
@@ -16,7 +16,7 @@
"pinia": "^2.0.17",
"prettier": "^3.0.3",
"vue": "^3.2.37",
"vue-i18n": "9"
"vue-i18n": "9.11.0"
},
"devDependencies": {
"@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>
</i18n-t>
<div class="text--grayed" v-if="store.vehiclesAPIData">
{{ $t('footer.version-check', { version: store.vehiclesAPIData.version }) }}
<div class="text--grayed" v-if="store.vehiclesData">
{{ $t('footer.version-check', { version: store.vehiclesData.simulatorVersion }) }}
</div>
<div>
+3 -4
View File
@@ -35,15 +35,14 @@ main {
background-color: darken($color: $bgColor, $amount: 5);
border-radius: 1em;
min-height: 950px;
padding: 1em;
}
@media screen and (max-width: $breakpointMd) {
main {
display: flex;
flex-direction: column;
grid-template-columns: 1fr;
grid-template-rows: 1fr;
display: block;
}
}
</style>
+15 -13
View File
@@ -9,8 +9,8 @@
{{ $t('realstock.title') }}
<a href="https://td2.info.pl/profile/?u=17708" target="_blank">Railtrains997</a>
</h1>
<button class="btn exit-btn" @click="store.isRealStockListCardOpen = false">
&Cross;
<button class="btn action-exit" @click="store.isRealStockListCardOpen = false">
<img src="/images/icon-exit.svg" alt="" />
</button>
</div>
@@ -47,7 +47,7 @@
</option>
</datalist>
<button class="btn" @click="resetStockFilters">
<button class="btn action-reset" @click="resetStockFilters">
{{ $t('realstock.action-reset') }}
</button>
</div>
@@ -100,13 +100,13 @@ import { useStore } from '../../store';
import imageMixin from '../../mixins/imageMixin';
import stockMixin from '../../mixins/stockMixin';
import { IRealComposition } from '../../types';
import { IRealComposition, VehicleGroupType } from '../../types';
function getVehicleType(stockType: string) {
if (/^E/.test(stockType)) return 'loco-e';
if (/^S/.test(stockType)) return 'loco-s';
function getVehicleType(stockType: string): VehicleGroupType {
if (/^E/.test(stockType)) return 'loco-electric';
if (/^S/.test(stockType)) return 'loco-diesel';
return 'car-passenger';
return 'wagon-passenger';
}
export default defineComponent({
@@ -222,13 +222,15 @@ export default defineComponent({
<style lang="scss" scoped>
@import '../../styles/global.scss';
.exit-btn {
font-size: 1.2em;
margin: 0.25em 0;
.action-exit {
display: flex;
background-color: #333;
border-radius: 0.25em;
padding: 0.5em;
}
.btn {
background-color: #444;
.action-reset {
background-color: #333;
}
.card_content {
+20 -19
View File
@@ -9,7 +9,7 @@
v-for="locoType in locomotiveTypeList"
:key="locoType.id"
class="btn btn--choice"
:data-selected="locoType.id == store.chosenLocoPower"
:data-selected="locoType.id == store.chosenLocoGroup"
@click="selectLocoType(locoType.id)"
>
{{ $t(`inputs.${locoType.id}`) }}
@@ -28,7 +28,7 @@
{{ $t('inputs.input-vehicle') }}
</option>
<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>
</select>
</div>
@@ -39,7 +39,7 @@
v-for="carType in carTypeList"
:key="carType.id"
class="btn btn--choice"
:data-selected="carType.id == store.chosenCarUseType"
:data-selected="carType.id == store.chosenCarGroup"
@click="selectCarWagonType(carType.id)"
>
{{ $t(`inputs.${carType.id}`) }}
@@ -59,7 +59,7 @@
</option>
<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>
</select>
</div>
@@ -70,7 +70,7 @@
id="cargo-select"
:disabled="
(store.chosenCar && !store.chosenCar.loadable) ||
(store.chosenCar && store.chosenCar.useType == 'car-passenger') ||
(store.chosenCar && store.chosenCar.group == 'wagon-passenger') ||
!store.chosenCar
"
data-select="cargo"
@@ -123,6 +123,7 @@ import imageMixin from '../../mixins/imageMixin';
import { useStore } from '../../store';
import stockPreviewMixin from '../../mixins/stockPreviewMixin';
import stockMixin from '../../mixins/stockMixin';
import { LocoGroupType, WagonGroupType } from '../../types';
export default defineComponent({
mixins: [imageMixin, stockPreviewMixin, stockMixin],
@@ -131,33 +132,33 @@ export default defineComponent({
store: useStore(),
locomotiveTypeList: [
{
id: 'loco-e',
id: 'loco-electric',
desc: 'ELEKTRYCZNE',
},
{
id: 'loco-s',
id: 'loco-diesel',
desc: 'SPALINOWE',
},
{
id: 'loco-ezt',
id: 'unit-electric',
desc: 'ELEKTR. ZESPOŁY TRAKCYJNE',
},
{
id: 'loco-szt',
id: 'unit-diesel',
desc: 'SPAL. ZESPOŁY TRAKCYJNE',
},
],
] as { id: LocoGroupType; desc: string }[],
carTypeList: [
{
id: 'car-passenger',
id: 'wagon-passenger',
desc: 'PASAŻERSKIE',
},
{
id: 'car-cargo',
id: 'wagon-freight',
desc: 'TOWAROWE',
},
],
] as { id: WagonGroupType; desc: string }[],
}),
computed: {
@@ -165,14 +166,14 @@ export default defineComponent({
return this.store.locoDataList
.slice()
.sort((a, b) => (a.type > b.type ? 1 : -1))
.filter((loco) => loco.power == this.store.chosenLocoPower);
.filter((loco) => loco.group == this.store.chosenLocoGroup);
},
carOptions() {
return this.store.carDataList
.slice()
.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;
},
selectLocoType(locoTypeId: string) {
this.store.chosenLocoPower = locoTypeId;
selectLocoType(locoGroupType: LocoGroupType) {
this.store.chosenLocoGroup = locoGroupType;
this.store.chosenVehicle = this.locoOptions[0];
this.store.chosenLoco = this.locoOptions[0];
},
selectCarWagonType(carWagonTypeId: string) {
this.store.chosenCarUseType = carWagonTypeId;
selectCarWagonType(wagonGroupType: WagonGroupType) {
this.store.chosenCarGroup = wagonGroupType;
this.store.chosenVehicle = this.carOptions[0];
this.store.chosenCar = this.carOptions[0];
this.store.chosenCargo = null;
-1
View File
@@ -51,7 +51,6 @@ export default {
.logo-section {
grid-row: 1;
grid-column: 1;
margin-bottom: 1.5em;
display: flex;
align-items: center;
+47 -38
View File
@@ -1,26 +1,27 @@
<template>
<section class="train-image-section">
<div class="train-image__content" :class="{ sponsor: store.chosenVehicle?.isSponsorsOnly }">
<div class="image-wrapper">
<img
tabindex="0"
:src="
store.chosenVehicle
? getThumbnailURL(store.chosenVehicle.type, 'small')
: '/images/placeholder.jpg'
"
tabindex="0"
:data-sponsor-only="store.chosenVehicle?.restrictions.sponsorOnly"
:data-team-only="store.chosenVehicle?.restrictions.teamOnly"
@click="onImageClick"
@keydown.enter="onImageClick"
@error="onImageError"
type="image/jpeg"
/>
</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 style="color: #ccc">
{{
$t(
`preview.${isLocomotive(store.chosenVehicle) ? store.chosenVehicle.power : store.chosenVehicle.useType}`
`preview.${isLocomotive(store.chosenVehicle) ? store.chosenVehicle.group : store.chosenVehicle.group}`
)
}}
</b>
@@ -37,30 +38,34 @@
<div v-else>
{{
store.chosenVehicle.useType == 'car-cargo'
store.chosenVehicle.group == 'wagon-freight'
? $t(`usage.${store.chosenVehicle.constructionType}`)
: `${$t('preview.construction')} ${store.chosenVehicle.constructionType}`
}}
</div>
<b style="color: salmon" v-if="store.chosenVehicle.isSponsorsOnly">{{
<b style="color: salmon" v-if="store.chosenVehicle.restrictions['sponsorOnly']">{{
$t('preview.sponsor-only', [
new Date(store.chosenVehicle.sponsorsOnlyTimestamp).toLocaleDateString(
new Date(store.chosenVehicle.restrictions['sponsorOnly']).toLocaleDateString(
$i18n.locale == 'pl' ? 'pl-PL' : 'en-GB'
),
])
}}</b>
<b style="color: gold" v-if="store.chosenVehicle.restrictions['teamOnly']">{{
$t('preview.team-only')
}}</b>
</div>
</div>
<div class="train-image__info" v-else>{{ $t('preview.desc') }}</div>
<div class="image-info" v-else>{{ $t('preview.desc') }}</div>
</section>
</template>
<script lang="ts">
import { computed, defineComponent } from 'vue';
import { useStore } from '../../store';
import { isLocomotive } from '../../utils/vehicleUtils';
import { isTractionUnit } from '../../utils/vehicleUtils';
import { ILocomotive, IVehicle } from '../../types';
import imageMixin from '../../mixins/imageMixin';
@@ -103,7 +108,7 @@ export default defineComponent({
},
isLocomotive(vehicle: IVehicle): vehicle is ILocomotive {
return isLocomotive(vehicle);
return isTractionUnit(vehicle);
},
onImageClick(e: Event) {
@@ -121,7 +126,7 @@ export default defineComponent({
</script>
<style lang="scss" scoped>
@import '../../styles/global.scss';
@import '../../styles/global';
.train-image-section {
display: flex;
@@ -135,24 +140,41 @@ export default defineComponent({
height: 22em;
}
.train-image {
&__content {
&.sponsor img {
border: 1px solid salmon;
}
img {
max-width: 380px;
width: 100%;
height: 100%;
border: 1px solid white;
img {
max-width: 380px;
width: 100%;
height: 100%;
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;
padding: 0.5em;
margin: 0.5em auto;
@@ -165,19 +187,6 @@ export default defineComponent({
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) {
.train-image-section {
justify-content: center;
+5 -6
View File
@@ -119,11 +119,10 @@ import { useStore } from '../../store';
import stockMixin from '../../mixins/stockMixin';
import { ICargo, ICarWagon, IStock } from '../../types';
import warningsMixin from '../../mixins/warningsMixin';
export default defineComponent({
name: 'stock-generator',
mixins: [stockMixin, warningsMixin],
mixins: [stockMixin],
data() {
return {
@@ -150,9 +149,9 @@ export default defineComponent({
},
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)
.sort((v1, v2) => this.$t(`cargo.${v1}`).localeCompare(this.$t(`cargo.${v2}`)))
@@ -196,7 +195,7 @@ export default defineComponent({
generateStock(empty = false) {
const generatedChosenStockList = this.chosenCargoTypes.reduce(
(acc, type) => {
this.store.vehiclesAPIData?.generator.cargo[type]
this.store.vehiclesData?.generator.cargo[type]
.filter((c) => !this.excludedCarTypes.includes(c.split(':')[0]))
.forEach((c) => {
const [type, cargoType] = c.split(':');
@@ -290,7 +289,7 @@ export default defineComponent({
this.store.chosenLoco = null;
this.store.chosenCargo = null;
if (c) this.store.chosenCarUseType = c?.useType;
if (c) this.store.chosenCarGroup = c?.group;
},
toggleCargoChosen(cargoType: string, vehicles: string[]) {
+148 -95
View File
@@ -115,20 +115,29 @@
</Checkbox>
</div>
<div class="stock_warnings" v-if="stockHasWarnings">
<div class="stock_warnings" v-if="hasAnyWarnings">
<div class="warning" v-if="locoNotSuitable">
(!) {{ $t('stocklist.warning-not-suitable') }}
</div>
<div class="warning" v-if="trainTooLong && store.isTrainPassenger">
<div class="warning" v-if="lengthExceeded && store.isTrainPassenger">
(!) {{ $t('stocklist.warning-passenger-too-long') }}
</div>
<div class="warning" v-if="trainTooLong && !store.isTrainPassenger">
<div class="warning" v-if="lengthExceeded && !store.isTrainPassenger">
(!) {{ $t('stocklist.warning-freight-too-long') }}
</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">
<template #href>
@@ -142,7 +151,7 @@
</i18n-t>
</div>
<div class="warning" v-if="tooManyLocomotives">
<div class="warning" v-if="locoCountExceeded">
{{ $t('stocklist.warning-too-many-locos') }}
</div>
</div>
@@ -150,52 +159,60 @@
<StockThumbnails :onListItemClick="onListItemClick" />
<!-- Stock list -->
<ul ref="stock_list">
<li v-if="stockIsEmpty" class="list-empty">
<div class="list-wrapper">
<div v-if="stockIsEmpty" class="list-empty">
<div class="stock-info">{{ $t('stocklist.list-empty') }}</div>
</li>
</div>
<TransitionGroup name="stock-list-anim" v-else>
<li
v-for="(stock, i) in store.stockList"
:key="stock.id"
:class="{ loco: stock.isLoco }"
tabindex="0"
@click="onListItemClick(i)"
@keydown.enter="onListItemClick(i)"
@keydown.w="moveUpStock(i)"
@keydown.s="moveDownStock(i)"
@keydown.backspace="removeStock(i)"
ref="itemRefs"
>
<div
class="stock-info"
@dragstart="onDragStart(i)"
@drop="onDrop($event, i)"
@dragover="allowDrop"
draggable="true"
<ul v-else>
<transition-group name="stock-list-anim">
<li
v-for="(stock, i) in store.stockList"
:key="stock.id"
:class="{ loco: stock.isLoco }"
tabindex="0"
@click="onListItemClick(i)"
@keydown.enter="onListItemClick(i)"
@keydown.w="moveUpStock(i)"
@keydown.s="moveDownStock(i)"
@keydown.backspace="removeStock(i)"
ref="itemRefs"
>
<span class="stock-info__no" :data-selected="i == store.chosenStockListIndex">
<span v-if="i == store.chosenStockListIndex">&bull;&nbsp;</span>
{{ i + 1 }}.
</span>
<span class="stock-info__type" :class="{ sponsor: stock.isSponsorsOnly }">
{{ stock.isLoco ? stock.type : getCarSpecFromType(stock.type) }}
</span>
<span class="stock-info__cargo" v-if="stock.cargo">
{{ stock.cargo.id }}
</span>
<span class="stock-info__length">{{ stock.length }}m</span>
<span class="stock-info__mass"
>{{ ((stock.weight + (stock.cargo?.weight ?? 0)) / 1000).toFixed(1) }}t</span
<div
class="stock-info"
@dragstart="onDragStart(i)"
@drop="onDrop($event, i)"
@dragover="allowDrop"
draggable="true"
>
<span class="stock-info__speed">{{ stock.maxSpeed }}km/h</span>
</div>
</li>
</TransitionGroup>
</ul>
<span class="stock-info-no" :data-selected="i == store.chosenStockListIndex">
<span v-if="i == store.chosenStockListIndex">&bull;&nbsp;</span>
{{ i + 1 }}.
</span>
<span
class="stock-info-type"
:data-sponsor-only="stock.restrictions.sponsorOnly"
:data-team-only="stock.restrictions.teamOnly"
>
{{ stock.isLoco ? stock.type : getCarSpecFromType(stock.type) }}
</span>
<span class="stock-info-cargo" v-if="stock.cargo">
{{ stock.cargo.id }}
</span>
<span class="stock-info-length">{{ stock.length }}m</span>
<span class="stock-info-mass">
{{ ((stock.weight + (stock.cargo?.weight ?? 0)) / 1000).toFixed(1) }}t
</span>
<span class="stock-info-speed">{{ stock.maxSpeed }}km/h</span>
</div>
</li>
</transition-group>
</ul>
</div>
</section>
</template>
@@ -204,7 +221,6 @@ import { defineComponent } from 'vue';
import { useStore } from '../../store';
import warningsMixin from '../../mixins/warningsMixin';
import imageMixin from '../../mixins/imageMixin';
import stockPreviewMixin from '../../mixins/stockPreviewMixin';
import StockThumbnails from '../utils/StockThumbnails.vue';
@@ -215,7 +231,7 @@ export default defineComponent({
name: 'stock-list',
components: { StockThumbnails, Checkbox },
mixins: [warningsMixin, imageMixin, stockMixin, stockPreviewMixin],
mixins: [imageMixin, stockMixin, stockPreviewMixin],
setup() {
const store = useStore();
@@ -233,6 +249,12 @@ export default defineComponent({
}),
computed: {
chosenRealComposition() {
const currentStockString = this.store.stockList.map((s) => s.type).join(';');
return this.store.realCompositionList.find((rc) => rc.stockString == currentStockString);
},
stockString() {
if (this.store.stockList.length == 0) return '';
@@ -263,16 +285,47 @@ export default defineComponent({
: this.store.stockList[this.store.chosenStockListIndex];
},
stockHasWarnings() {
lengthExceeded() {
return (
this.tooManyLocomotives || this.trainTooHeavy || this.trainTooLong || this.locoNotSuitable
(this.store.totalLength > 350 && this.store.isTrainPassenger) ||
(this.store.totalLength > 650 && !this.store.isTrainPassenger)
);
},
chosenRealComposition() {
const currentStockString = this.store.stockList.map((s) => s.type).join(';');
weightExceeded() {
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);
};
reader.onerror = (err) => console.log(err);
reader.onerror = (err) => console.error(err);
inputEl.value = '';
},
@@ -470,10 +523,12 @@ export default defineComponent({
display: flex;
flex-direction: column;
gap: 0.5em;
position: relative;
}
.warning {
padding: 0.25em;
margin: 0.25em 0;
background: $accentColor;
color: black;
@@ -536,10 +591,20 @@ export default defineComponent({
}
}
ul {
.list-wrapper {
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 {
@@ -556,16 +621,11 @@ ul > li {
&:focus-visible {
outline: 1px solid white;
}
&.list-empty {
background-color: $secondaryColor;
border-radius: 0.5em;
padding: 0.75em;
}
}
li > .stock-info {
display: flex;
gap: 0.25em;
color: white;
font-weight: 700;
@@ -574,46 +634,39 @@ li > .stock-info {
& > span {
padding: 0.5em;
margin-right: 0.25em;
display: flex;
justify-content: center;
align-items: center;
}
}
.sponsor {
color: salmon;
.stock-info-no,
.stock-info-type {
background-color: $secondaryColor;
&[data-team-only='true'] {
color: $teamColor;
}
&[data-sponsor-only] {
color: $sponsorColor;
}
}
.stock-info {
&__no,
&__type {
background-color: $secondaryColor;
}
.stock-info-no {
min-width: 3.5em;
text-align: right;
&__count {
background-color: #e04e3e;
&[data-selected='true'] {
color: $accentColor;
}
}
&__no {
min-width: 3.5em;
text-align: right;
.stock-info-cargo {
background-color: #333;
}
&[data-selected='true'] {
color: $accentColor;
}
}
&__cargo {
background-color: #333;
}
&__length,
&__mass,
&__speed {
background-color: #555;
}
.stock-info-length,
.stock-info-mass,
.stock-info-speed {
background-color: #555;
}
.stock-list-anim {
+75 -46
View File
@@ -45,43 +45,31 @@
<tbody>
<tr
v-for="{ vehicle, show } in computedTableData"
tabindex="0"
v-show="show"
tabindex="0"
:key="vehicle.type"
@click="previewVehicle(vehicle)"
@keydown.enter="previewVehicle(vehicle)"
@dblclick="addVehicle(vehicle)"
ref="itemRefs"
>
<td style="width: 120px">
<img
width="120"
:src="getThumbnailURL(vehicle.type, 'small')"
:alt="`${vehicle.type}`"
loading="lazy"
@error="(e) => ((e.target as HTMLElement).style.display = 'none')"
/>
<img width="120" src="" :data-src="getThumbnailURL(vehicle.type, 'small')" />
</td>
<td :data-sponsoronly="vehicle.isSponsorsOnly">
{{ vehicle.type }}
<td
:data-sponsor-only="vehicle.restrictions.sponsorOnly > 0"
:data-team-only="vehicle.restrictions.teamOnly"
style="min-width: 150px"
>
{{ vehicle.type.replace(/_/g, ' ') }}
</td>
<td v-if="isLocomotive(vehicle)">
{{ $t(`wiki.${vehicle.power}`) }}
</td>
<td v-else>{{ $t(`wiki.${vehicle.useType}`) }}</td>
<td style="min-width: 100px">{{ $t(`wiki.${vehicle.group}`) }}</td>
<td>{{ vehicle.constructionType }}</td>
<td>{{ vehicle.length }}m</td>
<td>{{ (vehicle.weight / 1000).toFixed(1) }}t</td>
<td>{{ vehicle.maxSpeed }}km/h</td>
<td v-if="currentFilterMode == 'carriages'">
{{ !isLocomotive(vehicle) ? vehicle.cargoTypes.length : '---' }}
</td>
<td v-if="currentFilterMode == 'tractions'">
{{ isLocomotive(vehicle) ? (vehicle.coldStart ? `&check;` : '&cross;') : '---' }}
</td>
<td>{{ vehicle.length }}</td>
<td>{{ (vehicle.weight / 1000).toFixed(1) }}</td>
<td>{{ vehicle.maxSpeed }}</td>
</tr>
</tbody>
@@ -97,7 +85,7 @@ import { defineComponent } from 'vue';
import { useStore } from '../../store';
import stockPreviewMixin from '../../mixins/stockPreviewMixin';
import { IVehicle } from '../../types';
import { isLocomotive } from '../../utils/vehicleUtils';
import { isTractionUnit } from '../../utils/vehicleUtils';
import stockMixin from '../../mixins/stockMixin';
import imageMixin from '../../mixins/imageMixin';
@@ -121,6 +109,7 @@ interface IWikiHeader {
interface IWikiRow {
vehicle: IVehicle;
show: boolean;
showImage: boolean;
}
const headers: IWikiHeader[] = [
@@ -131,8 +120,8 @@ const headers: IWikiHeader[] = [
{ id: 'length', sortable: true, for: 'all' },
{ id: 'weight', sortable: true, for: 'all' },
{ id: 'maxSpeed', sortable: true, for: 'all' },
{ id: 'coldStart', sortable: true, for: 'tractions' },
{ id: 'cargoCount', sortable: true, for: 'carriages' },
// { id: 'coldStart', sortable: true, for: 'tractions' },
// { id: 'cargoCount', sortable: true, for: 'carriages' },
];
export default defineComponent({
@@ -141,6 +130,7 @@ export default defineComponent({
data() {
return {
store: useStore(),
observer: null as IntersectionObserver | null,
headers,
scrollTop: 0,
@@ -156,6 +146,10 @@ export default defineComponent({
};
},
mounted() {
this.mountObserver();
},
activated() {
const tableWrapperRef = this.$refs['table-wrapper'] as HTMLElement;
@@ -165,10 +159,34 @@ export default defineComponent({
},
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) {
this.currentFilterMode = this.currentFilterMode == name ? 'all' : name;
const tableWrapperRef = this.$refs['table-wrapper'] as HTMLElement;
tableWrapperRef.scrollTo({
top: this.scrollTop,
});
},
toggleSorter(header: IWikiHeader) {
@@ -198,14 +216,16 @@ export default defineComponent({
case 'cargoCount':
return (
(!isLocomotive(row1.vehicle) ? Math.sign(row1.vehicle.cargoTypes.length || -1) : -1) -
(!isLocomotive(row2.vehicle) ? (row2.vehicle.cargoTypes.length || -1) * direction : -1)
Math.sign(
(!isTractionUnit(row1.vehicle) ? row1.vehicle.cargoTypes.length || -1 : -1) -
(!isTractionUnit(row2.vehicle) ? row2.vehicle.cargoTypes.length || -1 : -1)
) * direction
);
case 'coldStart':
return (
((isLocomotive(row1.vehicle) && row1.vehicle.coldStart ? 1 : -1) -
(isLocomotive(row2.vehicle) && row2.vehicle.coldStart ? 1 : -1)) *
((isTractionUnit(row1.vehicle) && row1.vehicle.coldStart ? 1 : -1) -
(isTractionUnit(row2.vehicle) && row2.vehicle.coldStart ? 1 : -1)) *
direction
);
@@ -224,11 +244,12 @@ export default defineComponent({
return this.store.vehicleDataList
.map((vehicle) => ({
vehicle,
showImage: false,
show:
new RegExp(`${this.searchedVehicleTypeName.trim()}`, 'i').test(vehicle.type) &&
(this.currentFilterMode == 'all' ||
(this.currentFilterMode == 'tractions' && isLocomotive(vehicle)) ||
(this.currentFilterMode == 'carriages' && !isLocomotive(vehicle))),
(this.currentFilterMode == 'tractions' && isTractionUnit(vehicle)) ||
(this.currentFilterMode == 'carriages' && !isTractionUnit(vehicle))),
}))
.sort((a, b) => this.sortTableRows(a, b));
},
@@ -260,8 +281,6 @@ export default defineComponent({
flex-wrap: wrap;
gap: 0.5em;
margin: 0.5em 0;
}
.actions-panel_vehicles {
@@ -275,10 +294,14 @@ export default defineComponent({
}
}
.tab_content {
display: grid;
grid-template-rows: 30px 770px;
gap: 0.5em;
}
.table-wrapper {
overflow: auto;
height: 750px;
max-height: 95vh;
}
.wiki-list table {
@@ -302,10 +325,6 @@ export default defineComponent({
cursor: pointer;
background-color: #333;
&:first-child {
min-width: 120px;
}
&:nth-child(odd) {
background-color: #444;
}
@@ -317,11 +336,21 @@ export default defineComponent({
td {
text-align: center;
height: 70px;
padding: 0.25em;
height: 75px;
&[data-sponsoronly='true'] {
color: salmon;
min-width: 95px;
&[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"
:key="stockIndex"
:data-selected="store.chosenStockListIndex == stockIndex"
:data-sponsor="stock.isSponsorsOnly"
:data-sponsor="stock.restrictions.sponsorOnly"
draggable="true"
@dragstart="onDragStart(stockIndex)"
@drop="onDrop($event, stockIndex)"
@@ -43,7 +43,7 @@ const onListItemClick = (index: number) => {
};
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(
-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({
baseURL:
import.meta.env.VITE_API_DEV === '1' && import.meta.env.DEV
? 'http://localhost:5500'
: 'https://static.spythere.eu',
? 'http://localhost:3001'
: 'https://stacjownik.spythere.eu',
});
export default http;
+22 -19
View File
@@ -15,12 +15,12 @@
"cargo-title": "Cargo (only selected freight cars)",
"no-cargo-available": "no cargo available",
"cargo-empty": "empty",
"loco-e": "ELECTR.",
"loco-s": "DIESEL",
"loco-ezt": "EMU",
"loco-szt": "DMU",
"car-passenger": "PASSENGER",
"car-cargo": "FREIGHT",
"loco-electric": "ELECTR.",
"loco-diesel": "DIESEL",
"unit-electric": "EMU",
"unit-diesel": "DMU",
"wagon-passenger": "PASSENGER",
"wagon-freight": "FREIGHT",
"action-add": "ADD NEW",
"action-swap": "SWAP WITH",
"real-stock": "POLISH TRAIN COMPOSITIONS"
@@ -30,12 +30,13 @@
"loading": "IMAGE LOADING...",
"desc": "Choose a railway vehicle above to see its preview",
"sponsor-only": "* SPONSORS ONLY UNTIL {0}",
"loco-e": "ELECTRIC LOCO",
"loco-s": "DIESEL LOCO",
"loco-ezt": "ELECTRIC M.U.",
"loco-szt": "DIESEL M.U.",
"car-passenger": "PASSENGER CARRIAGE",
"car-cargo": "FREIGHT CARRIAGE",
"team-only": "* TD2 TEAM ONLY",
"loco-electric": "ELECTRIC LOCO",
"loco-diesel": "DIESEL LOCO",
"unit-electric": "ELECTRIC M.U.",
"unit-diesel": "DIESEL M.U.",
"wagon-passenger": "PASSENGER CARRIAGE",
"wagon-freight": "FREIGHT CARRIAGE",
"cabin": "Cabin type:",
"construction": "Construction type:"
},
@@ -68,11 +69,13 @@
"coldstart-info": "Locomotive cold start",
"doublemanning-info": "Double manning",
"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-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-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)"
},
"stockgen": {
@@ -173,12 +176,12 @@
"maxSpeed": "Speed",
"cargoCount": "Cargo count"
},
"loco-ezt": "EMU",
"loco-szt": "DMU",
"loco-s": "Diesel locomotive",
"loco-e": "Electric locomotive",
"car-passenger": "Passenger carriage",
"car-cargo": "Frieght carriage"
"unit-electric": "EMU",
"unit-diesel": "DMU",
"loco-diesel": "Diesel locomotive",
"loco-electric": "Electric locomotive",
"wagon-passenger": "Passenger carriage",
"wagon-freight": "Frieght carriage"
},
"realstock": {
"title": "POLISH TRAIN COMPOSITIONS by",
+23 -20
View File
@@ -9,18 +9,18 @@
"version-check": "Strona jest kompletna dla wersji {version} symulatora TD2"
},
"inputs": {
"title": "WYBIERZ POJAZD SZYNOWY",
"title": "WYBIERZ POJAZD",
"input-vehicle": "Wybierz pojazd trakcyjny",
"input-carwagon": "Wybierz wagon",
"cargo-title": "Ładunek (tylko wybrane towarowe)",
"no-cargo-available": "brak dostępnych ładunków",
"cargo-empty": "próżny",
"loco-e": "ELEKTR.",
"loco-s": "SPAL.",
"loco-ezt": "EZT",
"loco-szt": "SZT",
"car-passenger": "PASAŻERSKIE",
"car-cargo": "TOWAROWE",
"loco-electric": "ELEKTR.",
"loco-diesel": "SPAL.",
"unit-electric": "EZT",
"unit-diesel": "SZT",
"wagon-passenger": "PASAŻERSKIE",
"wagon-freight": "TOWAROWE",
"action-add": "DODAJ NOWY",
"action-swap": "ZAMIEŃ ZA",
"real-stock": "REALNE ZESTAWIENIA"
@@ -30,12 +30,13 @@
"loading": "ŁADOWANIE OBRAZU...",
"desc": "Wybierz pojazd lub wagon, aby zobaczyć jego podgląd powyżej",
"sponsor-only": "* TYLKO DLA SPONSORÓW DO {0}",
"loco-e": "ELEKTROWÓZ",
"loco-s": "SPALINOWÓZ",
"loco-ezt": "EZT",
"loco-szt": "SZT",
"car-passenger": "WAGON PASAŻERSKI",
"car-cargo": "WAGON TOWAROWY",
"team-only": "* TYLKO DLA ZESPOŁU TD2",
"loco-electric": "ELEKTROWÓZ",
"loco-diesel": "SPALINOWÓZ",
"unit-electric": "EZT",
"unit-diesel": "SZT",
"wagon-passenger": "WAGON PASAŻERSKI",
"wagon-freight": "WAGON TOWAROWY",
"cabin": "Typ kabiny:",
"construction": "Typ konstrukcji:"
},
@@ -68,11 +69,13 @@
"coldstart-info": "Zimny start",
"doublemanning-info": "Podwójna obsada",
"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-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-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"
},
"stockgen": {
@@ -173,12 +176,12 @@
"maxSpeed": "Prędkość",
"cargoCount": "Ładunki"
},
"loco-ezt": "EZT",
"loco-szt": "SZT",
"loco-s": "Spalinowóz",
"loco-e": "Elektrowóz",
"car-passenger": "Wagon pasażerski",
"car-cargo": "Wagon towarowy"
"loco-diesel": "Spalinowóz",
"loco-electric": "Elektrowóz",
"unit-electric": "EZT",
"unit-diesel": "SZT",
"wagon-passenger": "Wagon pasażerski",
"wagon-freight": "Wagon towarowy"
},
"realstock": {
"title": "ZESTAWIENIA REALNE by",
+1 -5
View File
@@ -7,11 +7,7 @@ export default defineComponent({
},
getThumbnailURL(vehicleType: string, size: 'small' | 'large') {
return `${
import.meta.env.VITE_API_DEV === '1'
? 'http://localhost:5500'
: 'https://static.spythere.eu'
}/images/${vehicleType}--${size == 'small' ? 300 : 800}px.jpg`;
return `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 { useStore } from '../store';
import { ICarWagon, ILocomotive, IStock, ICargo, IVehicle } from '../types';
import { isLocomotive } from '../utils/vehicleUtils';
import { isTractionUnit } from '../utils/vehicleUtils';
export default defineComponent({
setup() {
@@ -16,7 +16,7 @@ export default defineComponent({
},
getStockObject(vehicle: IVehicle, cargo?: ICargo | null, count = 1): IStock {
const isLoco = isLocomotive(vehicle);
const isLoco = isTractionUnit(vehicle);
return {
id: this.getStockId(),
@@ -27,11 +27,9 @@ export default defineComponent({
isLoco,
cargo: !isLoco && vehicle.loadable && cargo ? cargo : undefined,
count,
imgSrc: vehicle.imageSrc,
useType: isLoco ? vehicle.power : vehicle.useType,
isSponsorsOnly: vehicle.isSponsorsOnly,
group: isLoco ? vehicle.group : vehicle.group,
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 (!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);
});
+8 -8
View File
@@ -1,7 +1,7 @@
import { defineComponent } from 'vue';
import { useStore } from '../store';
import { ICarWagon, ILocomotive, IStock, IVehicle } from '../types';
import { isLocomotive } from '../utils/vehicleUtils';
import { ICarWagon, ILocomotive, IStock, IVehicle, LocoGroupType, WagonGroupType } from '../types';
import { isTractionUnit } from '../utils/vehicleUtils';
export default defineComponent({
setup() {
@@ -14,40 +14,40 @@ export default defineComponent({
methods: {
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) {
const chosenLoco = this.store.locoDataList.find((v) => v.type == stock.type) || null;
this.store.chosenVehicle = chosenLoco;
this.store.chosenLoco = chosenLoco;
this.store.chosenCargo = null;
this.store.chosenLocoPower = stock.useType;
this.store.chosenLocoGroup = stock.group as LocoGroupType;
} else {
const chosenCar = this.store.carDataList.find((v) => v.type == stock.type) || null;
this.store.chosenVehicle = chosenCar;
this.store.chosenCar = chosenCar;
this.store.chosenCargo = stock.cargo || null;
this.store.chosenCarUseType = stock.useType;
this.store.chosenCarGroup = stock.group as WagonGroupType;
}
},
previewLocomotive(loco: ILocomotive) {
this.store.chosenLoco = loco;
this.store.chosenVehicle = loco;
this.store.chosenLocoPower = loco.power;
this.store.chosenLocoGroup = loco.group;
},
previewCarWagon(carWagon: ICarWagon) {
this.store.chosenCar = carWagon;
this.store.chosenCarUseType = carWagon.useType;
this.store.chosenCarGroup = carWagon.group;
this.store.chosenVehicle = carWagon;
this.store.chosenCargo = null;
},
previewVehicle(vehicle: IVehicle) {
if (isLocomotive(vehicle)) this.previewLocomotive(vehicle);
if (isTractionUnit(vehicle)) this.previewLocomotive(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 {
IVehiclesAPI,
IVehiclesData,
ICarWagon,
ILocomotive,
ICargo,
IVehicle,
IStock,
IRealComposition,
LocoGroupType,
WagonGroupType,
} from './types';
import { defineStore } from 'pinia';
import {
@@ -17,8 +19,9 @@ import {
totalLength,
totalWeight,
} from './utils/vehicleUtils';
import http from './http';
import i18n from './i18n-setup';
import http from './http';
export const useStore = defineStore({
id: 'store',
@@ -33,8 +36,8 @@ export const useStore = defineStore({
imageLoading: false,
chosenLocoPower: 'loco-e',
chosenCarUseType: 'car-passenger',
chosenLocoGroup: 'loco-electric' as LocoGroupType,
chosenCarGroup: 'wagon-passenger' as WagonGroupType,
stockList: [] as IStock[],
cargoOptions: [] as any[][],
@@ -50,17 +53,17 @@ export const useStore = defineStore({
isRandomizerCardOpen: false,
isRealStockListCardOpen: false,
vehiclesAPIData: undefined as IVehiclesAPI | undefined,
vehiclesData: undefined as IVehiclesData | undefined,
lastFocusedElement: null as HTMLElement | null,
}),
getters: {
locoDataList: (state) => locoDataList(state.vehiclesAPIData),
carDataList: (state) => carDataList(state.vehiclesAPIData),
locoDataList: (state) => locoDataList(state.vehiclesData),
carDataList: (state) => carDataList(state.vehiclesData),
vehicleDataList: (state) => [
...locoDataList(state.vehiclesAPIData),
...carDataList(state.vehiclesAPIData),
...locoDataList(state.vehiclesData),
...carDataList(state.vehiclesData),
],
totalWeight: (state) => totalWeight(state.stockList),
totalLength: (state) => totalLength(state.stockList),
@@ -69,16 +72,16 @@ export const useStore = defineStore({
acceptableWeight: (state) => acceptableWeight(state.stockList),
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) => {
const [type, number, ...name] = key.split(' ');
const obj = {
number: number.replace(/_/g, '/'),
name: name.join(' '),
stockString: state.vehiclesAPIData!.realCompositions[key],
stockString: state.vehiclesData!.realCompositions[key],
type,
};
@@ -100,9 +103,8 @@ export const useStore = defineStore({
const headingLoco = state.stockList[0];
return (
state.vehiclesAPIData?.vehicleProps.find(
(stock) => stock.type == headingLoco.constructionType
)?.coldStart ?? false
state.vehiclesData?.vehicleProps.find((stock) => stock.type == headingLoco.constructionType)
?.coldStart ?? false
);
},
@@ -113,9 +115,8 @@ export const useStore = defineStore({
const headingLoco = state.stockList[0];
return (
state.vehiclesAPIData?.vehicleProps.find(
(stock) => stock.type == headingLoco.constructionType
)?.doubleManned ?? false
state.vehiclesData?.vehicleProps.find((stock) => stock.type == headingLoco.constructionType)
?.doubleManned ?? false
);
},
},
@@ -123,8 +124,9 @@ export const useStore = defineStore({
actions: {
async fetchVehiclesAPI() {
try {
const vehiclesData = (await http.get<IVehiclesAPI>('/vehicles.json')).data;
this.vehiclesAPIData = vehiclesData;
const vehiclesData = (await http.get<IVehiclesData>('/vehicles')).data;
this.vehiclesData = vehiclesData;
} catch (error) {
console.error(error);
}
@@ -136,10 +138,10 @@ export const useStore = defineStore({
},
async mergeBackendTranslations() {
if (!this.vehiclesAPIData) return;
if (!this.vehiclesData) return;
i18n.global.mergeLocaleMessage('pl', this.vehiclesAPIData.vehicleLocales.pl);
i18n.global.mergeLocaleMessage('en', this.vehiclesAPIData.vehicleLocales.en);
i18n.global.mergeLocaleMessage('pl', this.vehiclesData.vehicleLocales.pl);
i18n.global.mergeLocaleMessage('en', this.vehiclesData.vehicleLocales.en);
},
handleRouting() {
+3
View File
@@ -6,6 +6,9 @@ $textColor: #fff;
$secondaryColor: #1b1b1b;
$accentColor: #e4c428;
$sponsorColor: salmon;
$teamColor: gold;
@font-face {
font-family: 'Lato';
src:
+17 -32
View File
@@ -1,16 +1,17 @@
export type IVehicle = ILocomotive | ICarWagon;
export type StockSectionMode = 'STOCK_LIST' | 'STOCK_GENERATOR';
export type TLocoGroup = 'loco-e' | 'loco-s' | 'loco-ezt' | 'loco-szt';
export type TCarWagonGroup = 'car-passenger' | 'car-cargo';
export type LocoGroupType = 'loco-electric' | 'loco-diesel' | 'unit-electric' | 'unit-diesel';
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;
speed: number;
length: number;
// mass: number;
weight: number;
// cargo?: string | null;
cargoTypes: ICargo[] | null;
cargoTypes?: ICargo[];
coldStart?: boolean;
doubleManned?: boolean;
}
@@ -20,8 +21,8 @@ export interface ICargo {
weight: number;
}
export interface IVehiclesAPI {
version: string;
export interface IVehiclesData {
simulatorVersion: string;
generator: {
cargo: {
@@ -29,16 +30,9 @@ export interface IVehiclesAPI {
};
};
vehicleInfo: {
'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][];
};
vehicleList: any[][];
vehicleProps: IStockProps[];
vehicleProps: IVehicleProps[];
vehicleLocales: {
pl: {
@@ -56,14 +50,11 @@ export interface IVehiclesAPI {
export interface ILocomotive {
type: string;
power: TLocoGroup;
group: TLocoGroup;
group: LocoGroupType;
constructionType: string;
cabinType: string;
maxSpeed: number;
isSponsorsOnly: boolean;
sponsorsOnlyTimestamp: number;
imageSrc: string;
restrictions: Record<RestrictionType, any>;
weight: number;
length: number;
coldStart: boolean;
@@ -72,14 +63,11 @@ export interface ILocomotive {
export interface ICarWagon {
type: string;
useType: TCarWagonGroup;
group: TCarWagonGroup;
group: WagonGroupType;
constructionType: string;
loadable: boolean;
isSponsorsOnly: boolean;
sponsorsOnlyTimestamp: number;
restrictions: Record<RestrictionType, any>;
maxSpeed: number;
imageSrc: string;
weight: number;
length: number;
cargoTypes: ICargo[];
@@ -88,18 +76,15 @@ export interface ICarWagon {
export interface IStock {
id: string;
type: string;
useType: string;
group: LocoGroupType | WagonGroupType;
constructionType: string;
length: number;
// mass: number;
weight: number;
maxSpeed: number;
cargo?: ICargo;
isLoco: boolean;
isSponsorsOnly: boolean;
sponsorsOnlyTimestamp: number;
restrictions: Record<RestrictionType, any>;
count: number;
imgSrc?: string;
}
export interface IRealComposition {
+55 -58
View File
@@ -1,5 +1,11 @@
import { EVehicleUseType } from '../enums/EVehicleUseType';
import { ICarWagon, ILocomotive, IStock, IVehiclesAPI, TCarWagonGroup, TLocoGroup } from '../types';
import {
ICarWagon,
ILocomotive,
IStock,
IVehiclesData,
LocoGroupType,
WagonGroupType,
} from '../types';
import {
MassLimitLocoType,
SpeedLimitLocoType,
@@ -7,86 +13,77 @@ import {
calculateSpeedLimit,
} from './vehicleLimitsUtils';
export function isLocomotive(vehicle: ILocomotive | ICarWagon): vehicle is ILocomotive {
return (vehicle as ILocomotive).power !== undefined;
export function isTractionUnit(vehicle: ILocomotive | ICarWagon): vehicle is ILocomotive {
return (vehicle as ILocomotive).cabinType !== undefined;
}
export function locoDataList(vehiclesData: IVehiclesAPI | undefined) {
export function locoDataList(vehiclesData: IVehiclesData | undefined) {
if (!vehiclesData) return [];
return Object.keys(vehiclesData.vehicleInfo).reduce((acc, vehiclePower) => {
if (!vehiclePower.startsWith('loco')) return acc;
return vehiclesData.vehicleList.reduce<ILocomotive[]>((acc, vehicleInfoArray) => {
// 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;
const locoProps = vehiclesData.vehicleProps.find((prop) => constructionType == prop.type);
locoVehiclesData.forEach((loco) => {
// if (!loco[4]) return;
if (!locoProps) {
console.warn('Brak atrybutów dla pojazdu:', type);
return acc;
}
const [type, constructionType, cabinType, maxSpeed, sponsorsTimestamp] = loco;
const locoProps = vehiclesData.vehicleProps.find((prop) => constructionType == prop.type);
acc.push({
group: group as LocoGroupType,
acc.push({
power: vehiclePower as TLocoGroup,
group: vehiclePower as TLocoGroup,
type,
constructionType,
cabinType,
maxSpeed: Number(maxSpeed),
isSponsorsOnly: Number(sponsorsTimestamp) > Date.now(),
sponsorsOnlyTimestamp: Number(sponsorsTimestamp),
imageSrc: '',
type,
constructionType,
cabinType,
length:
locoProps?.length && type.startsWith('2EN')
? locoProps.length * 2
: locoProps?.length ?? 0,
weight: locoProps?.weight && type.startsWith('2EN') ? 253000 : locoProps?.weight ?? 0,
restrictions: restrictions ?? {},
coldStart: locoProps?.coldStart ?? false,
doubleManned: locoProps?.doubleManned ?? false,
});
maxSpeed: locoProps.speed,
length: locoProps.length,
weight: locoProps.weight,
coldStart: locoProps.coldStart ?? false,
doubleManned: locoProps.doubleManned ?? false,
});
return acc;
}, [] as ILocomotive[]);
}, []);
}
export function carDataList(vehiclesData: IVehiclesAPI | undefined) {
export function carDataList(vehiclesData: IVehiclesData | undefined) {
if (!vehiclesData) return [];
return Object.keys(vehiclesData.vehicleInfo).reduce((acc, vehicleUseType) => {
if (!vehicleUseType.startsWith('car')) return acc;
return vehiclesData.vehicleList.reduce<ICarWagon[]>((acc, vehicleInfoArray) => {
// 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) => {
const [type, constructionType, loadable, sponsorsOnlyTimestamp, maxSpeed] = car;
if (!wagonProps) {
console.warn('Brak atrybutów dla pojazdu:', type);
return acc;
}
if (sponsorsOnlyTimestamp && Number(sponsorsOnlyTimestamp) <= Date.now()) return;
acc.push({
group: group as WagonGroupType,
type,
constructionType,
loadable: wagonProps.cargoTypes ? wagonProps.cargoTypes.length > 0 : false,
cargoTypes: wagonProps?.cargoTypes ?? [],
const carPropsData = vehiclesData.vehicleProps.find((v) =>
type.toString().startsWith(v.type)
);
restrictions: restrictions ?? {},
acc.push({
useType: vehicleUseType as TCarWagonGroup,
group: vehicleUseType as TCarWagonGroup,
type,
constructionType,
loadable,
isSponsorsOnly: Number(sponsorsOnlyTimestamp) > Date.now(),
sponsorsOnlyTimestamp: Number(sponsorsOnlyTimestamp),
maxSpeed: Number(maxSpeed),
imageSrc: '',
cargoTypes: carPropsData?.cargoTypes ?? [],
weight: carPropsData?.weight || 0,
length: carPropsData?.length || 0,
});
maxSpeed: wagonProps.speed,
weight: wagonProps?.weight || 0,
length: wagonProps?.length || 0,
});
return acc;
}, [] as ICarWagon[]);
}, []);
}
export function totalWeight(stockList: IStock[]) {
@@ -142,5 +139,5 @@ export function isTrainPassenger(stockList: IStock[]) {
return stockList
.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({
registerType: 'autoUpdate',
includeAssets: ['/images/*.{png,svg,jpg}', '/fonts/*.{woff,woff2,ttf}'],
devOptions: {
suppressWarnings: true,
enabled: true,
@@ -23,7 +25,7 @@ export default defineConfig({
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',
options: {
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',
options: {
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],
},
},
},
],
},
}),