mirror of
https://github.com/Spythere/stacjownik.git
synced 2026-05-03 05:18:11 +00:00
Compare commits
14 Commits
development
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 4fa7459e39 | |||
| 0a88880e98 | |||
| cfe8deff8b | |||
| a34eef098b | |||
| 97a829a21c | |||
| 83444f64d0 | |||
| c2f7eef146 | |||
| 3c78af4dc0 | |||
| fc7a9be9dd | |||
| 3c3a114a38 | |||
| fe6972c1f8 | |||
| 08b9b72dcd | |||
| c90be042e7 | |||
| 430a05ab38 |
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "stacjownik",
|
||||
"version": "1.35.0",
|
||||
"version": "1.34.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 22 KiB |
@@ -1,9 +1,7 @@
|
||||
<template>
|
||||
<Card :is-open="isUpdateCardOpen" @toggle-card="toggleCard(false)">
|
||||
<div class="content" tabindex="0" ref="content">
|
||||
<h1 class="content-title">
|
||||
<i class="fa-solid fa-wand-sparkles"></i> {{ $t('update.title') }}
|
||||
</h1>
|
||||
<h1 class="content-title"><i class="fa-solid fa-wand-sparkles"></i> {{ $t('update.title') }}</h1>
|
||||
|
||||
<div class="features-body" v-if="htmlChangelog != ''" v-html="htmlChangelog"></div>
|
||||
<div class="no-features" v-else>{{ $t('update.no-data') }}</div>
|
||||
@@ -89,27 +87,13 @@ export default defineComponent({
|
||||
|
||||
::v-deep(h2) {
|
||||
padding: 0.5em 0;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
display: block;
|
||||
height: 2px;
|
||||
width: 100%;
|
||||
background-color: #aaa;
|
||||
margin-top: 0.25em;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep(h3) {
|
||||
padding-bottom: 0.25em;
|
||||
border-bottom: 1px solid #aaa;
|
||||
}
|
||||
|
||||
::v-deep(ul) {
|
||||
list-style: disc;
|
||||
padding: 0.5em 1.5em;
|
||||
line-height: 1.5em;
|
||||
padding: 0 1.5em;
|
||||
|
||||
padding-bottom: 0.5em;
|
||||
}
|
||||
|
||||
.content {
|
||||
|
||||
@@ -1,325 +0,0 @@
|
||||
<template>
|
||||
<div class="driver-propositions">
|
||||
<h3>{{ t('trains.number-propositions-header') }}</h3>
|
||||
|
||||
<div class="categories-select">
|
||||
<button
|
||||
v-for="(category, i) in availableCategories"
|
||||
class="btn btn--option btn--action"
|
||||
@click="selectCategory(i)"
|
||||
:class="{ checked: i == chosenCategoryIndex }"
|
||||
>
|
||||
{{ category }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-if="numberPropositions.length > 0" class="propositions-numbers">
|
||||
<div v-if="chosenCategory">
|
||||
<b>{{ chosenCategory }} </b> -
|
||||
{{ t(`categories.${chosenCategory.slice(0, 2)}`) }}
|
||||
({{ t(`categories.${chosenCategory.slice(2)}`) }})
|
||||
</div>
|
||||
|
||||
<div v-if="chosenCategoryRules">
|
||||
<span v-if="chosenCategoryRules[0]"
|
||||
>{{ t('trains.number-propositions-third-number') }}
|
||||
<b class="text--primary">{{ chosenCategoryRules[0] }}</b> •
|
||||
</span>
|
||||
|
||||
<span
|
||||
>{{
|
||||
t('trains.number-propositions-last-nums', {
|
||||
count: chosenCategoryRules[1].length
|
||||
})
|
||||
}}
|
||||
<b class="text--primary">{{ chosenCategoryRules[1] }}</b> -
|
||||
<b class="text--primary">{{ chosenCategoryRules[2] }}</b></span
|
||||
>
|
||||
</div>
|
||||
|
||||
<div style="margin-top: 0.5em">
|
||||
<b>{{ t('trains.number-propositions-title') }} </b>
|
||||
<i>{{ numberPropositions.join(', ') }}</i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="no-propositions" v-else>{{ t('trains.number-propositions-empty') }}</div>
|
||||
|
||||
<div class="cargo-warnings" v-if="getCargoWarnings.size > 0">
|
||||
<hr />
|
||||
<h3>{{ t('cargo-warnings.title') }}</h3>
|
||||
|
||||
<div class="warnings-container">
|
||||
<div
|
||||
v-for="warning in getCargoWarnings"
|
||||
class="train-badge"
|
||||
:class="`${warning.split('-')[0]}`"
|
||||
>
|
||||
{{ t('cargo-warnings.' + warning) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, PropType, watch, onMounted } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { Train } from '../../typings/common';
|
||||
import rulesJSON from '../../data/trainNumberRules.json';
|
||||
import { useApiStore } from '../../store/apiStore';
|
||||
|
||||
const { t } = useI18n();
|
||||
const apiStore = useApiStore();
|
||||
|
||||
const props = defineProps({
|
||||
chosenTrain: {
|
||||
type: Object as PropType<Train>,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
const emits = defineEmits(['selectCategory']);
|
||||
|
||||
const chosenCategoryIndex = ref(0);
|
||||
|
||||
const numberPropositions = ref<string[]>([]);
|
||||
const chosenCategoryRules = ref<any[]>([]);
|
||||
|
||||
watch(
|
||||
computed(() => props.chosenTrain.trainNo),
|
||||
() => {
|
||||
chosenCategoryIndex.value = 0;
|
||||
generateNumberPropositions();
|
||||
}
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
generateNumberPropositions();
|
||||
});
|
||||
|
||||
function generateNumberPropositions() {
|
||||
const categoryCode = chosenCategory.value?.slice(0, 2);
|
||||
const trainNoStr = props.chosenTrain.trainNo.toString();
|
||||
|
||||
// Get category rules
|
||||
const rules = categoryCode
|
||||
? ((rulesJSON.categoriesRules as any)[categoryCode] as any[])
|
||||
: undefined;
|
||||
|
||||
if (!categoryCode || !rules) {
|
||||
numberPropositions.value.length = 0;
|
||||
chosenCategoryRules.value.length = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const [thirdNumber, minRange, maxRange] = rules;
|
||||
|
||||
const propositionsArr: string[] = [];
|
||||
|
||||
for (let i = 0; i < 5; i++) {
|
||||
let generatedNumStr = '';
|
||||
|
||||
generatedNumStr += trainNoStr[0] ?? Math.floor(Math.random() * 10);
|
||||
generatedNumStr += trainNoStr[1] ?? Math.floor(Math.random() * 10);
|
||||
|
||||
// Third number
|
||||
generatedNumStr += thirdNumber ?? '';
|
||||
|
||||
// Remaining numbers
|
||||
const rangeNums = minRange?.length ?? 3;
|
||||
|
||||
const randRange = Math.floor(
|
||||
Math.random() * (Number(maxRange) - Number(minRange)) + Number(minRange)
|
||||
).toString();
|
||||
|
||||
const leadingZeros = new Array(Math.abs(randRange.toString().length - rangeNums))
|
||||
.fill('0')
|
||||
.join('');
|
||||
|
||||
generatedNumStr += `${leadingZeros}${randRange}`;
|
||||
|
||||
const isNumberTaken =
|
||||
apiStore.activeData?.trains?.some((t) => t.trainNo.toString() == generatedNumStr) ?? false;
|
||||
|
||||
if (!isNumberTaken) {
|
||||
propositionsArr.push(generatedNumStr);
|
||||
} else {
|
||||
i--;
|
||||
}
|
||||
|
||||
if (Number(randRange) > Number(maxRange)) break;
|
||||
}
|
||||
|
||||
numberPropositions.value = propositionsArr;
|
||||
chosenCategoryRules.value = rules;
|
||||
}
|
||||
|
||||
const chosenCategory = computed(() => {
|
||||
return availableCategories.value[chosenCategoryIndex.value];
|
||||
});
|
||||
|
||||
const getCargoWarnings = computed(() => {
|
||||
const stockList = props.chosenTrain.stockList;
|
||||
|
||||
let warnings: Set<string> = new Set();
|
||||
|
||||
stockList.forEach((stockVehicle) => {
|
||||
const [vehicleName, vehicleCargo] = stockVehicle.split(':');
|
||||
|
||||
if (vehicleName.startsWith('WB117')) warnings.add(vehicleCargo ? 'twr-un1965' : 'tn-un1965');
|
||||
else if (vehicleName.startsWith('445Rb'))
|
||||
warnings.add(vehicleCargo ? 'tn-un1202' : 'tn-un1202-empty');
|
||||
else if (vehicleName.startsWith('EDK80')) warnings.add('pn-edk80');
|
||||
|
||||
if (vehicleCargo) {
|
||||
if (vehicleCargo.startsWith('wt_20')) warnings.add('pn-innofreight');
|
||||
else if (/^(tank|vehicles_01|truck)/.test(vehicleCargo)) warnings.add('pn-military');
|
||||
}
|
||||
});
|
||||
|
||||
return warnings;
|
||||
});
|
||||
|
||||
const availableCategories = computed(() => {
|
||||
const stockList = props.chosenTrain.stockList;
|
||||
const headVehicle = stockList[0]?.split('-')[0] ?? '';
|
||||
|
||||
let availableCategories: string[] = [];
|
||||
let categoryTraction = 'E';
|
||||
|
||||
let vehicleTypesSet = new Set<string>();
|
||||
let wagonsNamesSet = new Set<string>();
|
||||
let cargoNamesSet = new Set<string>();
|
||||
|
||||
for (const stockName of stockList) {
|
||||
const [vehicleName, ...cargoList] = stockName.split(':');
|
||||
|
||||
const vehicleData = apiStore.vehiclesData?.vehicles.find((v) => v.name == vehicleName);
|
||||
|
||||
if (!vehicleData) continue;
|
||||
|
||||
vehicleTypesSet.add(vehicleData.type);
|
||||
|
||||
if (vehicleData.type.startsWith('wagon-')) wagonsNamesSet.add(vehicleData.name.split('_')[0]);
|
||||
|
||||
if (cargoList !== undefined) cargoList.forEach((c) => cargoNamesSet.add(c.split('_')[0]));
|
||||
}
|
||||
|
||||
let vehicleTypesArr = [...vehicleTypesSet];
|
||||
let wagonsNamesArr = [...wagonsNamesSet];
|
||||
|
||||
// Traction
|
||||
if (vehicleTypesArr[0] == 'loco-electric') categoryTraction = 'E';
|
||||
else if (vehicleTypesArr[0] == 'loco-diesel') categoryTraction = 'S';
|
||||
else if (vehicleTypesArr[0] == 'unit-electric') categoryTraction = 'J';
|
||||
else categoryTraction = 'M';
|
||||
|
||||
// EMU / DMU - M*, R*, P*
|
||||
if (vehicleTypesArr.length == 1 && (categoryTraction == 'J' || categoryTraction == 'M')) {
|
||||
availableCategories.push('MO', 'MP', 'MM', 'RO', 'RP', 'RA', 'RM', 'PW');
|
||||
}
|
||||
// Only locos (up to 3) - LT, LP, LS
|
||||
else if (stockList.length <= 3 && vehicleTypesArr.every((v) => v.startsWith('loco-'))) {
|
||||
if (/^(EU|ET|201E|4E|SU|ST|M62|CTLR4C)/.test(headVehicle)) availableCategories.push('LT');
|
||||
if (/^(EU|EP|SU|SP)/.test(headVehicle)) availableCategories.push('LP');
|
||||
if (/^(SM)/.test(headVehicle)) availableCategories.push('LS');
|
||||
}
|
||||
// Only locos (more than 3) - TH
|
||||
else if (stockList.length > 3 && vehicleTypesArr.every((v) => v.startsWith('loco-'))) {
|
||||
availableCategories.push('TH');
|
||||
}
|
||||
// Loco(s) + passenger only wagons - M*, R*, E*, P*
|
||||
else if (vehicleTypesArr.every((v) => v.startsWith('loco-') || v == 'wagon-passenger')) {
|
||||
availableCategories.push('EI', 'EC', 'EN', 'MO', 'MP', 'MM', 'RO', 'RP', 'RA', 'RM', 'PW');
|
||||
}
|
||||
// Loco(s) + cargo only / mixed wagons - T*, Z*
|
||||
else {
|
||||
if (wagonsNamesArr.every((v) => /^(627Z|412Z)/.test(v)))
|
||||
availableCategories.push('TC', 'TD', 'TS');
|
||||
else if (stockList.slice(1).every((v) => /PKPE/.test(v))) {
|
||||
availableCategories.push('ZU', 'ZN');
|
||||
} else if (wagonsNamesArr.length < 3 || cargoNamesSet.size < 3) {
|
||||
availableCategories.push('TM', 'TG', 'TS', 'TK');
|
||||
} else {
|
||||
availableCategories.push('TN', 'TR', 'TS', 'TK');
|
||||
}
|
||||
}
|
||||
|
||||
return availableCategories.map((c) => `${c}${categoryTraction}`);
|
||||
});
|
||||
|
||||
function selectCategory(i: number) {
|
||||
chosenCategoryIndex.value = i;
|
||||
generateNumberPropositions();
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@use '../../styles/responsive';
|
||||
@use '../../styles/badge';
|
||||
|
||||
.driver-propositions {
|
||||
margin-bottom: 1em;
|
||||
padding: 0.5em;
|
||||
background-color: #111;
|
||||
}
|
||||
|
||||
.categories-select {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5em;
|
||||
margin-top: 0.5em;
|
||||
|
||||
position: relative;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: calc(-0.5em);
|
||||
left: 0;
|
||||
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
background-color: #aaa;
|
||||
}
|
||||
}
|
||||
|
||||
.propositions-numbers {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
.no-propositions {
|
||||
margin-top: 1em;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.cargo-warnings {
|
||||
margin-top: 0.5em;
|
||||
|
||||
h3 {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
}
|
||||
|
||||
.warnings-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5em;
|
||||
}
|
||||
|
||||
@include responsive.smallScreen {
|
||||
.driver-propositions {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.categories-select {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.warnings-container {
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -4,18 +4,63 @@
|
||||
|
||||
<!-- Train action buttons -->
|
||||
<div class="train-stock-actions">
|
||||
<button class="btn btn--action" @click="copyStockToClipboard()">
|
||||
<button class="btn btn--action" style="margin: 1em 0" @click="copyStockToClipboard()">
|
||||
<i class="fa-regular fa-copy"></i> {{ i18n.t('trains.stock-copy') }}
|
||||
</button>
|
||||
|
||||
<button class="btn btn--action" @click="toggleNumberPropositions()">
|
||||
<button class="btn btn--action" style="margin: 1em 0" @click="toggleNumberPropositions()">
|
||||
<i class="fa-regular fa-lightbulb"></i> {{ i18n.t('trains.number-propositions') }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Proposed numbers container -->
|
||||
<transition name="view-anim">
|
||||
<DriverPropositions :chosenTrain="chosenTrain" v-if="arePropositionsVisible" />
|
||||
<transition name="view-anim" class="propositions-container">
|
||||
<div v-if="arePropositionsVisible">
|
||||
<h3 style="margin-bottom: 0.5em">{{ i18n.t('trains.number-propositions-header') }}</h3>
|
||||
|
||||
<div class="categories-select">
|
||||
<button
|
||||
v-for="(category, i) in availableCategories"
|
||||
class="btn btn--option btn--action"
|
||||
@click="selectCategory(i)"
|
||||
:class="{ checked: i == chosenCategoryIndex }"
|
||||
>
|
||||
{{ category }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-if="numberPropositions.length > 0" class="propositions-numbers">
|
||||
<div v-if="chosenCategory">
|
||||
<b>{{ chosenCategory }} </b> -
|
||||
{{ i18n.t(`categories.${chosenCategory.slice(0, 2)}`) }}
|
||||
({{ i18n.t(`categories.${chosenCategory.slice(2)}`) }})
|
||||
</div>
|
||||
|
||||
<div v-if="chosenCategoryRules">
|
||||
<span v-if="chosenCategoryRules[0]"
|
||||
>{{ i18n.t('trains.number-propositions-third-number') }}
|
||||
<b class="text--primary">{{ chosenCategoryRules[0] }}</b> •
|
||||
</span>
|
||||
|
||||
<span
|
||||
>{{
|
||||
i18n.t('trains.number-propositions-last-nums', {
|
||||
count: chosenCategoryRules[1].length
|
||||
})
|
||||
}}
|
||||
<b class="text--primary">{{ chosenCategoryRules[1] }}</b> -
|
||||
<b class="text--primary">{{ chosenCategoryRules[2] }}</b></span
|
||||
>
|
||||
</div>
|
||||
|
||||
<div style="margin-top: 0.5em">
|
||||
<b>{{ i18n.t('trains.number-propositions-title') }} </b>
|
||||
<i>{{ numberPropositions.join(', ') }}</i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="no-propositions" v-else>{{ i18n.t('trains.number-propositions-empty') }}</div>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<StockList :trainStockList="chosenTrain.stockList" :key="chosenTrain.id" :showPreviews="true" />
|
||||
@@ -27,15 +72,25 @@
|
||||
import { PropType, ref } from 'vue';
|
||||
import { Train } from '../../typings/common';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useApiStore } from '../../store/apiStore';
|
||||
|
||||
import StockList from '../Global/StockList.vue';
|
||||
import TrainSchedule from '../TrainsView/TrainSchedule.vue';
|
||||
import TrainInfo from '../TrainsView/TrainInfo.vue';
|
||||
import DriverPropositions from './DriverPropositions.vue';
|
||||
|
||||
import rulesJSON from '../../data/trainNumberRules.json';
|
||||
import { computed } from 'vue';
|
||||
import { watch } from 'vue';
|
||||
|
||||
const apiStore = useApiStore();
|
||||
|
||||
const i18n = useI18n();
|
||||
|
||||
const arePropositionsVisible = ref(false);
|
||||
const chosenCategoryIndex = ref(0);
|
||||
|
||||
const numberPropositions = ref<string[]>([]);
|
||||
const chosenCategoryRules = ref<any[]>([]);
|
||||
|
||||
const props = defineProps({
|
||||
chosenTrain: {
|
||||
@@ -64,7 +119,153 @@ function copyStockToClipboard() {
|
||||
|
||||
function toggleNumberPropositions() {
|
||||
arePropositionsVisible.value = !arePropositionsVisible.value;
|
||||
|
||||
if (arePropositionsVisible.value) generateNumberPropositions();
|
||||
}
|
||||
|
||||
function selectCategory(i: number) {
|
||||
chosenCategoryIndex.value = i;
|
||||
|
||||
generateNumberPropositions();
|
||||
}
|
||||
|
||||
function generateNumberPropositions() {
|
||||
const categoryCode = chosenCategory.value?.slice(0, 2);
|
||||
const trainNoStr = props.chosenTrain.trainNo.toString();
|
||||
|
||||
// Get category rules
|
||||
const rules = categoryCode
|
||||
? ((rulesJSON.categoriesRules as any)[categoryCode] as any[])
|
||||
: undefined;
|
||||
|
||||
if (!categoryCode || !rules) {
|
||||
numberPropositions.value.length = 0;
|
||||
chosenCategoryRules.value.length = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const [thirdNumber, minRange, maxRange] = rules;
|
||||
|
||||
const propositionsArr: string[] = [];
|
||||
|
||||
for (let i = 0; i < 5; i++) {
|
||||
let generatedNumStr = '';
|
||||
|
||||
generatedNumStr += trainNoStr.at(0) ?? Math.floor(Math.random() * 10);
|
||||
generatedNumStr += trainNoStr.at(1) ?? Math.floor(Math.random() * 10);
|
||||
|
||||
// Third number
|
||||
generatedNumStr += thirdNumber ?? '';
|
||||
|
||||
// Remaining numbers
|
||||
const rangeNums = minRange?.length ?? 3;
|
||||
|
||||
const randRange = Math.floor(
|
||||
Math.random() * (Number(maxRange) - Number(minRange)) + Number(minRange)
|
||||
).toString();
|
||||
|
||||
const leadingZeros = new Array(Math.abs(randRange.toString().length - rangeNums))
|
||||
.fill('0')
|
||||
.join('');
|
||||
|
||||
generatedNumStr += `${leadingZeros}${randRange}`;
|
||||
|
||||
const isNumberTaken =
|
||||
apiStore.activeData?.trains?.some((t) => t.trainNo.toString() == generatedNumStr) ?? false;
|
||||
|
||||
if (!isNumberTaken) {
|
||||
propositionsArr.push(generatedNumStr);
|
||||
} else {
|
||||
i--;
|
||||
}
|
||||
|
||||
if (Number(randRange) > Number(maxRange)) break;
|
||||
}
|
||||
|
||||
numberPropositions.value = propositionsArr;
|
||||
chosenCategoryRules.value = rules;
|
||||
}
|
||||
|
||||
const chosenCategory = computed(() => {
|
||||
return availableCategories.value.at(chosenCategoryIndex.value);
|
||||
});
|
||||
|
||||
const availableCategories = computed(() => {
|
||||
const stockList = props.chosenTrain.stockList;
|
||||
const headVehicle = stockList.at(0)?.split('-')[0] ?? '';
|
||||
|
||||
let availableCategories: string[] = [];
|
||||
let categoryTraction = 'E';
|
||||
|
||||
let vehicleTypesSet = new Set<string>();
|
||||
let wagonsNamesSet = new Set<string>();
|
||||
let cargoNamesSet = new Set<string>();
|
||||
|
||||
for (const stockName of stockList) {
|
||||
const [vehicleName, ...cargoList] = stockName.split(':');
|
||||
|
||||
const vehicleData = apiStore.vehiclesData?.vehicles.find((v) => v.name == vehicleName);
|
||||
|
||||
if (!vehicleData) continue;
|
||||
|
||||
vehicleTypesSet.add(vehicleData.type);
|
||||
|
||||
if (vehicleData.type.startsWith('wagon-')) wagonsNamesSet.add(vehicleData.name.split('_')[0]);
|
||||
|
||||
if (cargoList !== undefined) cargoList.forEach((c) => cargoNamesSet.add(c.split('_')[0]));
|
||||
}
|
||||
|
||||
let vehicleTypesArr = [...vehicleTypesSet];
|
||||
let wagonsNamesArr = [...wagonsNamesSet];
|
||||
|
||||
// Traction
|
||||
if (vehicleTypesArr[0] == 'loco-electric') categoryTraction = 'E';
|
||||
else if (vehicleTypesArr[0] == 'loco-diesel') categoryTraction = 'S';
|
||||
else if (vehicleTypesArr[0] == 'unit-electric') categoryTraction = 'J';
|
||||
else categoryTraction = 'M';
|
||||
|
||||
// EMU / DMU - M*, R*, P*
|
||||
if (vehicleTypesArr.length == 1 && (categoryTraction == 'J' || categoryTraction == 'M')) {
|
||||
availableCategories.push('MO', 'MP', 'MM', 'RO', 'RP', 'RA', 'RM', 'PW');
|
||||
}
|
||||
// Only locos (up to 3) - LT, LP, LS
|
||||
else if (stockList.length <= 3 && vehicleTypesArr.every((v) => v.startsWith('loco-'))) {
|
||||
if (/^(EU|ET|201E|4E|SU|ST|M62|CTLR4C)/.test(headVehicle)) availableCategories.push('LT');
|
||||
if (/^(EU|EP|SU|SP)/.test(headVehicle)) availableCategories.push('LP');
|
||||
if (/^(SM)/.test(headVehicle)) availableCategories.push('LS');
|
||||
}
|
||||
// Only locos (more than 3) - TH
|
||||
else if (stockList.length > 3 && vehicleTypesArr.every((v) => v.startsWith('loco-'))) {
|
||||
availableCategories.push('TH');
|
||||
}
|
||||
// Loco(s) + passenger only wagons - M*, R*, E*, P*
|
||||
else if (vehicleTypesArr.every((v) => v.startsWith('loco-') || v == 'wagon-passenger')) {
|
||||
availableCategories.push('EI', 'EC', 'EN', 'MO', 'MP', 'MM', 'RO', 'RP', 'RA', 'RM', 'PW');
|
||||
}
|
||||
// Loco(s) + cargo only / mixed wagons - T*, Z*
|
||||
else {
|
||||
if (wagonsNamesArr.every((v) => /^(627Z|412Z)/.test(v)))
|
||||
availableCategories.push('TC', 'TD', 'TS');
|
||||
else if (stockList.slice(1).every((v) => /PKPE/.test(v))) {
|
||||
availableCategories.push('ZU', 'ZN');
|
||||
} else if (wagonsNamesArr.length < 3 || cargoNamesSet.size < 3) {
|
||||
availableCategories.push('TM', 'TG', 'TS', 'TK');
|
||||
} else {
|
||||
availableCategories.push('TN', 'TR', 'TS', 'TK');
|
||||
}
|
||||
}
|
||||
|
||||
return availableCategories.map((c) => `${c}${categoryTraction}`);
|
||||
});
|
||||
|
||||
watch(
|
||||
computed(() => `${props.chosenTrain.trainNo}`),
|
||||
() => {
|
||||
chosenCategoryIndex.value = 0;
|
||||
generateNumberPropositions();
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@@ -78,13 +279,49 @@ function toggleNumberPropositions() {
|
||||
|
||||
.train-stock-actions {
|
||||
display: flex;
|
||||
gap: 0.5em;
|
||||
}
|
||||
|
||||
.propositions-container {
|
||||
margin-bottom: 1em;
|
||||
padding: 0.5em;
|
||||
background-color: #111;
|
||||
}
|
||||
|
||||
.categories-select {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5em;
|
||||
margin: 1em 0;
|
||||
|
||||
position: relative;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: calc(-0.5em);
|
||||
left: 0;
|
||||
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
background-color: #aaa;
|
||||
}
|
||||
}
|
||||
|
||||
.propositions-numbers {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
.no-propositions {
|
||||
margin-top: 1em;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
@include responsive.smallScreen {
|
||||
.train-stock-actions {
|
||||
.propositions-container {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.categories-select {
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,20 +18,7 @@
|
||||
</b>
|
||||
|
||||
<span
|
||||
v-if="isCreator(entry.dispatcherName)"
|
||||
data-tooltip-type="CreatorTooltip"
|
||||
:data-tooltip-content="$t('donations.creator-message')"
|
||||
>
|
||||
<router-link
|
||||
class="text--creator"
|
||||
:to="`/journal/dispatchers?search-dispatcher=${entry.dispatcherName}`"
|
||||
>
|
||||
{{ entry.dispatcherName }}
|
||||
</router-link>
|
||||
</span>
|
||||
|
||||
<span
|
||||
v-else-if="apiStore.donatorsData.includes(entry.dispatcherName)"
|
||||
v-if="apiStore.donatorsData.includes(entry.dispatcherName)"
|
||||
data-tooltip-type="DonatorTooltip"
|
||||
:data-tooltip-content="$t('donations.dispatcher-message')"
|
||||
>
|
||||
@@ -135,7 +122,6 @@ import styleMixin from '../../../mixins/styleMixin';
|
||||
import { useApiStore } from '../../../store/apiStore';
|
||||
import StationStatusBadge from '../../Global/StationStatusBadge.vue';
|
||||
import FlagIcon from '../../Global/FlagIcon.vue';
|
||||
import { isCreator } from '../../../utils/userUtils';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
@@ -148,7 +134,7 @@ export default defineComponent({
|
||||
emits: ['toggleShowExtraInfo'],
|
||||
|
||||
data() {
|
||||
return { regions, apiStore: useApiStore(), isCreator };
|
||||
return { regions, apiStore: useApiStore() };
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
@@ -64,7 +64,6 @@ function navigateToProfile() {
|
||||
<style lang="scss" scoped>
|
||||
@use '../../styles/dropdown';
|
||||
@use '../../styles/dropdown-filters';
|
||||
@use '../../styles/responsive';
|
||||
|
||||
.dropdown_wrapper {
|
||||
left: auto;
|
||||
@@ -72,10 +71,4 @@ function navigateToProfile() {
|
||||
max-width: 700px;
|
||||
top: 3.5em;
|
||||
}
|
||||
|
||||
@include responsive.smallScreen {
|
||||
.dropdown_wrapper {
|
||||
top: 6.25em;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -59,17 +59,7 @@
|
||||
</strong>
|
||||
|
||||
<router-link
|
||||
v-if="isCreator(timetable.driverName)"
|
||||
class="text--creator"
|
||||
data-tooltip-type="CreatorTooltip"
|
||||
:data-tooltip-content="$t('donations.creator-message')"
|
||||
:to="`/journal/timetables?search-driver=${timetable.driverName}`"
|
||||
>
|
||||
<strong>{{ timetable.driverName }}</strong>
|
||||
</router-link>
|
||||
|
||||
<router-link
|
||||
v-else-if="apiStore.donatorsData.includes(timetable.driverName)"
|
||||
v-if="apiStore.donatorsData.includes(timetable.driverName)"
|
||||
class="text--donator"
|
||||
data-tooltip-type="DonatorTooltip"
|
||||
:data-tooltip-content="$t('donations.driver-message')"
|
||||
@@ -125,7 +115,6 @@ import styleMixin from '../../../mixins/styleMixin';
|
||||
import { useApiStore } from '../../../store/apiStore';
|
||||
import trainCategoryMixin from '../../../mixins/trainCategoryMixin';
|
||||
import FlagIcon from '../../Global/FlagIcon.vue';
|
||||
import { isCreator } from '../../../utils/userUtils';
|
||||
|
||||
export default defineComponent({
|
||||
components: { FlagIcon },
|
||||
@@ -133,8 +122,7 @@ export default defineComponent({
|
||||
|
||||
data() {
|
||||
return {
|
||||
apiStore: useApiStore(),
|
||||
isCreator
|
||||
apiStore: useApiStore()
|
||||
};
|
||||
},
|
||||
|
||||
|
||||
@@ -5,10 +5,7 @@
|
||||
<ProfilePlayerAvatar :playerTD2Info="playerTD2Info" />
|
||||
|
||||
<div>
|
||||
<h2
|
||||
class="player-name-header"
|
||||
:class="{ 'text--donator': isPlayerDonator, 'text--creator': isPlayerCreator }"
|
||||
>
|
||||
<h2 class="player-name-header" :class="{ 'text--donator': isPlayerDonator }">
|
||||
<a :href="`https://td2.info.pl/profile/?u=${route.query.playerId}`" target="_blank">
|
||||
<img
|
||||
v-if="isPlayerDonator"
|
||||
@@ -235,7 +232,6 @@ import { useApiStore } from '../../store/apiStore';
|
||||
import StationStatusBadge from '../Global/StationStatusBadge.vue';
|
||||
import ProfilePlayerAvatar from './ProfilePlayerAvatar.vue';
|
||||
import { getRegionNameById } from '../../utils/regionUtils';
|
||||
import { isCreator } from '../../utils/userUtils';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
@@ -261,8 +257,6 @@ const isPlayerDonator = computed(() =>
|
||||
props.playerName ? apiStore.donatorsData.includes(props.playerName) : false
|
||||
);
|
||||
|
||||
const isPlayerCreator = computed(() => (props.playerName ? isCreator(props.playerName) : false));
|
||||
|
||||
const activeDispatches = computed(() => {
|
||||
if (!props.playerName) return [];
|
||||
if (!apiStore.activeData || !apiStore.activeData.activeSceneries) return [];
|
||||
|
||||
@@ -24,6 +24,12 @@ import { useRoute, useRouter } from 'vue-router';
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
const prevPath = ref('/');
|
||||
|
||||
onMounted(() => {
|
||||
prevPath.value = (route.meta['prevPath'] as string) ?? '/';
|
||||
});
|
||||
|
||||
defineProps({
|
||||
station: {
|
||||
type: Object as PropType<Station>
|
||||
@@ -40,7 +46,7 @@ defineProps({
|
||||
});
|
||||
|
||||
function onReturnButtonClick() {
|
||||
router.push('/');
|
||||
router.push(prevPath.value);
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -9,16 +9,9 @@
|
||||
</span>
|
||||
|
||||
<router-link class="dispatcher-name" :to="`/profile?playerId=${onlineScenery.dispatcherId}`">
|
||||
<span
|
||||
class="text--creator"
|
||||
v-if="isCreator(onlineScenery.dispatcherName)"
|
||||
:title="$t('donations.creator-message')"
|
||||
>
|
||||
{{ onlineScenery.dispatcherName }}
|
||||
</span>
|
||||
<span
|
||||
class="text--donator"
|
||||
v-else-if="apiStore.donatorsData.includes(onlineScenery.dispatcherName)"
|
||||
v-if="apiStore.donatorsData.includes(onlineScenery.dispatcherName)"
|
||||
:title="$t('donations.dispatcher-message')"
|
||||
>
|
||||
{{ onlineScenery.dispatcherName }}
|
||||
@@ -58,7 +51,6 @@ import StationStatusBadge from '../../Global/StationStatusBadge.vue';
|
||||
import { ActiveScenery } from '../../../typings/common';
|
||||
import { useApiStore } from '../../../store/apiStore';
|
||||
import FlagIcon from '../../Global/FlagIcon.vue';
|
||||
import { isCreator } from '../../../utils/userUtils';
|
||||
|
||||
export default defineComponent({
|
||||
mixins: [styleMixin, dateMixin, routerMixin],
|
||||
@@ -66,8 +58,7 @@ export default defineComponent({
|
||||
|
||||
data() {
|
||||
return {
|
||||
apiStore: useApiStore(),
|
||||
isCreator
|
||||
apiStore: useApiStore()
|
||||
};
|
||||
},
|
||||
|
||||
|
||||
@@ -28,15 +28,14 @@
|
||||
<Loading v-if="listState == Status.Data.Loading" />
|
||||
<div v-else-if="listState == Status.Data.Error">Ups, coś poszło nie tak...</div>
|
||||
|
||||
<ul v-else-if="bestScoreList.length > 0">
|
||||
<ul v-else>
|
||||
<li v-for="(value, i) in bestScoreList">
|
||||
<div>
|
||||
{{ t('ordinal', { count: i + 1 }) }} {{ t('scenery.top-list.place') }} -
|
||||
{{ t('scenery.top-list.place', i + 1) }} -
|
||||
<router-link :to="`/profile?playerId=${value.dispatcherId}`">{{
|
||||
value.dispatcherName
|
||||
}}</router-link>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<b class="text--primary" v-if="currentListMode == 'dutyCount'">{{
|
||||
t('scenery.top-list.duty-count', value.value)
|
||||
@@ -53,11 +52,6 @@
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div v-else class="no-data">
|
||||
<span v-if="currentListScope == 'name'">{{ t('scenery.top-list.no-data-general') }}</span>
|
||||
<span v-else>{{ t('scenery.top-list.no-data-current-hash') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -173,6 +167,10 @@ async function fetchTopDispatchersList() {
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5em;
|
||||
font-weight: bold;
|
||||
|
||||
button {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.rating-list-wrapper {
|
||||
@@ -203,11 +201,4 @@ async function fetchTopDispatchersList() {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.no-data {
|
||||
padding: 1em 0.5em;
|
||||
font-size: 1.1em;
|
||||
background-color: #333;
|
||||
color: #ccc;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<section class="station_table" @scroll="onScroll" ref="tableRef">
|
||||
<section class="station_table">
|
||||
<Loading
|
||||
v-if="apiStore.dataStatuses.connection == Status.Loading && filteredStationList.length == 0"
|
||||
/>
|
||||
@@ -131,16 +131,7 @@
|
||||
<td class="station-dispatcher-name">
|
||||
<span v-if="station.onlineInfo?.dispatcherName">
|
||||
<b
|
||||
v-if="isCreator(station.onlineInfo.dispatcherName)"
|
||||
data-tooltip-type="CreatorTooltip"
|
||||
:data-tooltip-content="$t('donations.creator-message')"
|
||||
>
|
||||
<img src="/images/icon-creator.png" alt="creator icon" />
|
||||
<span class="text--creator"> {{ station.onlineInfo.dispatcherName }}</span>
|
||||
</b>
|
||||
|
||||
<b
|
||||
v-else-if="apiStore.donatorsData.includes(station.onlineInfo.dispatcherName)"
|
||||
v-if="apiStore.donatorsData.includes(station.onlineInfo.dispatcherName)"
|
||||
data-tooltip-type="DonatorTooltip"
|
||||
:data-tooltip-content="$t('donations.dispatcher-message')"
|
||||
>
|
||||
@@ -362,7 +353,6 @@ import { ActiveSorter, HeadIdsType, headIconsIds, headIds } from './typings';
|
||||
import { filterStations, sortStations } from './utils';
|
||||
import { getLanguageNameById } from '../../utils/languageUtils';
|
||||
import FlagIcon from '../Global/FlagIcon.vue';
|
||||
import { isCreator } from '../../utils/userUtils';
|
||||
|
||||
export default defineComponent({
|
||||
emits: ['toggleDonationCard'],
|
||||
@@ -373,9 +363,7 @@ export default defineComponent({
|
||||
data: () => ({
|
||||
headIconsIds,
|
||||
headIds,
|
||||
scrollTop: 0,
|
||||
getChangedFilters,
|
||||
isCreator
|
||||
getChangedFilters
|
||||
}),
|
||||
|
||||
setup() {
|
||||
@@ -403,10 +391,6 @@ export default defineComponent({
|
||||
};
|
||||
},
|
||||
|
||||
activated() {
|
||||
(this.$refs['tableRef'] as HTMLElement).scrollTop = this.scrollTop;
|
||||
},
|
||||
|
||||
methods: {
|
||||
getSceneryRoute(station: Station) {
|
||||
this.$router.push({
|
||||
@@ -447,10 +431,6 @@ export default defineComponent({
|
||||
}));
|
||||
|
||||
return JSON.stringify(usersTrains);
|
||||
},
|
||||
|
||||
onScroll(e: Event) {
|
||||
this.scrollTop = (e.target as HTMLElement).scrollTop;
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -633,8 +613,8 @@ tbody tr {
|
||||
|
||||
.station-dispatcher-name {
|
||||
img {
|
||||
max-height: 1.3em;
|
||||
vertical-align: text-top;
|
||||
max-width: 1.35em;
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
<template>
|
||||
<div class="tooltip-content">
|
||||
<img src="/images/icon-creator.png" alt="creator icon" />
|
||||
<b class="text--creator"> {{ tooltipStore.content }}</b>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { useTooltipStore } from '../../store/tooltipStore';
|
||||
|
||||
export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
tooltipStore: useTooltipStore()
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.tooltip-content {
|
||||
padding: 0.5em;
|
||||
border-radius: 0.25em;
|
||||
|
||||
width: 100%;
|
||||
|
||||
background-color: #333;
|
||||
box-shadow: 0 0 10px 2px #aaa;
|
||||
}
|
||||
|
||||
img {
|
||||
vertical-align: text-bottom;
|
||||
height: 1.25em;
|
||||
}
|
||||
</style>
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="tooltip-content">
|
||||
<img src="/images/icon-diamond.svg" alt="donator diamond icon" />
|
||||
<b class="text--donator"> {{ tooltipStore.content }}</b>
|
||||
<span>{{ tooltipStore.content }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -20,6 +20,11 @@ export default defineComponent({
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.tooltip-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5em;
|
||||
flex-wrap: wrap;
|
||||
|
||||
padding: 0.5em;
|
||||
border-radius: 0.25em;
|
||||
|
||||
@@ -30,7 +35,7 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
img {
|
||||
vertical-align: text-bottom;
|
||||
height: 1.25em;
|
||||
vertical-align: middle;
|
||||
height: 1em;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -8,13 +8,12 @@
|
||||
import { defineComponent } from 'vue';
|
||||
import { useTooltipStore } from '../../store/tooltipStore';
|
||||
import DonatorTooltip from './DonatorTooltip.vue';
|
||||
import CreatorTooltip from './CreatorTooltip.vue';
|
||||
import VehiclePreviewTooltip from './VehiclePreviewTooltip.vue';
|
||||
import BaseTooltip from './BaseTooltip.vue';
|
||||
import SpawnsTooltip from './SpawnsTooltip.vue';
|
||||
import UsersTooltip from './UsersTooltip.vue';
|
||||
import HtmlTooltip from './HtmlTooltip.vue';
|
||||
import TrainInfoTooltip from './TrainInfoTooltip.vue';
|
||||
import TrainInfoTooltip from "./TrainInfoTooltip.vue";
|
||||
|
||||
const BOX_PADDING_PX = 20;
|
||||
|
||||
@@ -26,8 +25,7 @@ export default defineComponent({
|
||||
SpawnsTooltip,
|
||||
UsersTooltip,
|
||||
HtmlTooltip,
|
||||
TrainInfoTooltip,
|
||||
CreatorTooltip
|
||||
TrainInfoTooltip
|
||||
},
|
||||
|
||||
data() {
|
||||
|
||||
@@ -56,21 +56,12 @@
|
||||
</b>
|
||||
|
||||
<b
|
||||
v-if="isCreator(train.driverName)"
|
||||
data-tooltip-type="CreatorTooltip"
|
||||
:data-tooltip-content="$t('donations.creator-message')"
|
||||
>
|
||||
<img src="/images/icon-creator.png" alt="creator icon" />
|
||||
<span class="text--creator"> {{ train.driverName }}</span>
|
||||
</b>
|
||||
|
||||
<b
|
||||
v-else-if="apiStore.donatorsData.includes(train.driverName)"
|
||||
v-if="apiStore.donatorsData.includes(train.driverName)"
|
||||
data-tooltip-type="DonatorTooltip"
|
||||
:data-tooltip-content="$t('donations.driver-message')"
|
||||
>
|
||||
<span class="text--donator">{{ train.driverName }} </span>
|
||||
<img src="/images/icon-diamond.svg" alt="donator diamond icon" />
|
||||
<span class="text--donator"> {{ train.driverName }}</span>
|
||||
</b>
|
||||
|
||||
<span v-else>{{ train.driverName }}</span>
|
||||
@@ -213,7 +204,6 @@ import trainCategoryMixin from '../../mixins/trainCategoryMixin';
|
||||
import ProgressBar from '../Global/ProgressBar.vue';
|
||||
import StockList from '../Global/StockList.vue';
|
||||
import FlagIcon from '../Global/FlagIcon.vue';
|
||||
import { isCreator } from '../../utils/userUtils';
|
||||
|
||||
export default defineComponent({
|
||||
mixins: [trainInfoMixin, styleMixin, trainCategoryMixin],
|
||||
@@ -232,8 +222,7 @@ export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
store: useMainStore(),
|
||||
apiStore: useApiStore(),
|
||||
isCreator
|
||||
apiStore: useApiStore()
|
||||
};
|
||||
},
|
||||
|
||||
@@ -289,6 +278,8 @@ export default defineComponent({
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-size: 1em;
|
||||
gap: 0.25em;
|
||||
|
||||
background-color: #1a1a1a;
|
||||
gap: 0.5em;
|
||||
}
|
||||
@@ -367,6 +358,8 @@ export default defineComponent({
|
||||
.status-badges {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin-left: 0.25em;
|
||||
|
||||
gap: 0.25em;
|
||||
|
||||
img {
|
||||
@@ -379,7 +372,7 @@ export default defineComponent({
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5em;
|
||||
padding: 0.25em 0;
|
||||
padding: 0.5em 0;
|
||||
}
|
||||
|
||||
.progress-distance {
|
||||
|
||||
+22
-33
@@ -3,6 +3,25 @@ import plLang from './locales/pl.json';
|
||||
|
||||
import { createI18n } from 'vue-i18n';
|
||||
|
||||
function customRule(choice: number, choicesLength: number) {
|
||||
if (choice === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const teen = choice > 10 && choice < 20;
|
||||
const endsWithOne = choice % 10 === 1;
|
||||
|
||||
if (!teen && endsWithOne) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!teen && choice % 10 >= 2 && choice % 10 <= 4) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
return choicesLength < 4 ? 2 : 3;
|
||||
}
|
||||
|
||||
const i18n = createI18n({
|
||||
locale: 'pl',
|
||||
legacy: false,
|
||||
@@ -10,42 +29,12 @@ const i18n = createI18n({
|
||||
fallbackLocale: 'pl',
|
||||
|
||||
pluralizationRules: {
|
||||
en: {
|
||||
ordinal: (ctx: { named: (arg0: string) => number }) => {
|
||||
const number = ctx.named('count');
|
||||
|
||||
const suffixes: Record<number, string> = {
|
||||
1: 'st',
|
||||
2: 'nd',
|
||||
3: 'rd'
|
||||
};
|
||||
const suffix = suffixes[number % 10] || 'th';
|
||||
|
||||
return `${number}${suffix}`;
|
||||
}
|
||||
}
|
||||
pl: customRule
|
||||
},
|
||||
|
||||
messages: {
|
||||
en: {
|
||||
...enLang,
|
||||
ordinal: (ctx: { named: (arg0: string) => number }) => {
|
||||
const number = ctx.named('count');
|
||||
|
||||
const suffixes: Record<number, string> = {
|
||||
1: 'st',
|
||||
2: 'nd',
|
||||
3: 'rd'
|
||||
};
|
||||
const suffix = suffixes[number % 10] || 'th';
|
||||
|
||||
return `${number}${suffix}`;
|
||||
}
|
||||
},
|
||||
pl: {
|
||||
...plLang,
|
||||
ordinal: '{count}.'
|
||||
}
|
||||
en: enLang,
|
||||
pl: plLang
|
||||
},
|
||||
enableLegacy: false
|
||||
});
|
||||
|
||||
+7
-21
@@ -42,8 +42,7 @@
|
||||
"action-paypal": "DONATE WITH PAYPAL",
|
||||
"action-buycoffee": "BUY ME A COFFEE!",
|
||||
"dispatcher-message": "Dispatcher supporting the Stacjownik project!",
|
||||
"driver-message": "Driver supporting the Stacjownik project!",
|
||||
"creator-message": "Creator of the Stacjownik project"
|
||||
"driver-message": "Driver supporting the Stacjownik project!"
|
||||
},
|
||||
"warnings": {
|
||||
"TWR": "Train with high risk cargo",
|
||||
@@ -439,25 +438,15 @@
|
||||
"driver-not-found-others": "Player {driver} is online as:",
|
||||
"driver-not-found-return": "RETURN TO THE MAIN SITE",
|
||||
"stock-copy": "COPY THE STOCK",
|
||||
"number-propositions": "NUMBER & WARNINGS SUGGESTIONS",
|
||||
"number-propositions": "PROPOSE NUMBER",
|
||||
"stock-clipboard-success": "Successfully copied the railway stock in a text form to your clipboard!",
|
||||
"stock-clipboard-failure": "Oops! Something happened and the railway stock couldn't be copied to your clipboard! :/",
|
||||
"number-propositions-header": "Generate number examples for a train category:",
|
||||
"number-propositions-header": "Generate number examples for selected category:",
|
||||
"number-propositions-third-number": "Third digit:",
|
||||
"number-propositions-last-nums": "{count} last digits from the range of:",
|
||||
"number-propositions-title": "Propositions:",
|
||||
"number-propositions-empty": "No propositions available for the chosen category! :/"
|
||||
},
|
||||
"cargo-warnings": {
|
||||
"title": "Additional cargo warnings:",
|
||||
"pn-innofreight": "PN: Innofreight C45: exceeded gauge",
|
||||
"twr-un1965": "TWR: UN1965 (LPG)",
|
||||
"tn-un1965": "TN: unclean tanks after UN1965",
|
||||
"tn-un1202": "TN: UN1202 (diesel fuel)",
|
||||
"tn-un1202-empty": "TN: unclean tanks after UN1202",
|
||||
"pn-military": "PN: military transport",
|
||||
"pn-edk80": "PN: EDK80 railway crane"
|
||||
},
|
||||
"train-stats": {
|
||||
"stats-button": "STATISTICS",
|
||||
"title": "ONLINE TRAINS STATS",
|
||||
@@ -570,7 +559,7 @@
|
||||
"no-users": "NO ACTIVE PLAYERS",
|
||||
"no-spawns": "NO OPEN SPAWNS",
|
||||
"no-scenery": "Oops! This scenery doesn't exist!",
|
||||
"return-btn": "BACK TO SCENERIES",
|
||||
"return-btn": "BACK TO THE MAIN SITE",
|
||||
"history-btn": "View the dispatcher history",
|
||||
"info-btn": "Return to the scenery view",
|
||||
"authors-title": "Scenery author | Scenery authors",
|
||||
@@ -598,7 +587,7 @@
|
||||
"dispatcher-rate": "Rate:",
|
||||
"dispatcher-status-changes": "Status changes:",
|
||||
"req-level": "all dispatcher levels | dispatcher level {lvl} required | dispatcher level {lvl} required",
|
||||
"history-list-empty": "No saved scenery history!",
|
||||
"history-list-empty": "No recorded scenery history!",
|
||||
"forum-topic": "Scenery's forum topic",
|
||||
"gnr-link": "Train orders generator",
|
||||
"pragotron-link": "Timetable pallet board",
|
||||
@@ -614,13 +603,10 @@
|
||||
"scope-name": "GENERAL",
|
||||
"scope-hash": "CURRENT HASH",
|
||||
|
||||
"place": "place",
|
||||
"place": "{n}. place",
|
||||
"dispatcher-rating": "Rating: {n}",
|
||||
"duty-count": "No duties | 1 duty | Duties: {n}",
|
||||
"duration": "Duration:",
|
||||
|
||||
"no-data-general": "No best scores for this scenery on the PL1 server!",
|
||||
"no-data-current-hash": "No best scores for the current scenery hash on the PL1 server!"
|
||||
"duration": "Duration:"
|
||||
}
|
||||
},
|
||||
"availability": {
|
||||
|
||||
+6
-20
@@ -42,8 +42,7 @@
|
||||
"action-paypal": "PRZELEJ PAYPALEM",
|
||||
"action-buycoffee": "POSTAW KAWĘ!",
|
||||
"dispatcher-message": "Dyżurny wspierający projekt Stacjownika!",
|
||||
"driver-message": "Maszynista wspierający projekt Stacjownika!",
|
||||
"creator-message": "Twórca projektu Stacjownik"
|
||||
"driver-message": "Maszynista wspierający projekt Stacjownika!"
|
||||
},
|
||||
"warnings": {
|
||||
"TWR": "Pociąg z towarami niebezpiecznymi wysokiego ryzyka",
|
||||
@@ -426,25 +425,15 @@
|
||||
"driver-not-found-others": "Gracz {driver} jest online jako:",
|
||||
"driver-not-found-return": "WRÓĆ NA STRONĘ GŁÓWNĄ",
|
||||
"stock-copy": "SKOPIUJ SKŁAD",
|
||||
"number-propositions": "PROPOZYCJE NUMERÓW I UWAG",
|
||||
"number-propositions": "ZAPROPONUJ NUMER",
|
||||
"stock-clipboard-success": "Pomyślnie skopiowano skład w postaci tekstowej do schowka!",
|
||||
"stock-clipboard-failure": "Ups! Nie udało się skopiować składu do schowka! :/",
|
||||
"number-propositions-header": "Wygeneruj propozycje numerów dla pociągu kategorii:",
|
||||
"number-propositions-header": "Wygeneruj propozycje numerów dla kategorii pociągu:",
|
||||
"number-propositions-third-number": "Trzecia cyfra:",
|
||||
"number-propositions-last-nums": "{count} ostatnie cyfry z przedziału:",
|
||||
"number-propositions-title": "Propozycje:",
|
||||
"number-propositions-empty": "Brak propozycji dla wybranej kategorii! :/"
|
||||
},
|
||||
"cargo-warnings": {
|
||||
"title": "Dodatkowe uwagi przewozowe:",
|
||||
"pn-innofreight": "PN: Innofreight C45: przekroczona skrajnia",
|
||||
"twr-un1965": "TWR: UN1965 (LPG)",
|
||||
"tn-un1965": "TN: brudne cysterny po UN1965",
|
||||
"tn-un1202": "TN: UN1202 (olej napędowy)",
|
||||
"tn-un1202-empty": "TN: brudne cysterny po UN1202",
|
||||
"pn-military": "PN: transport wojskowy",
|
||||
"pn-edk80": "PN: żuraw kolejowy EDK80"
|
||||
},
|
||||
"train-stats": {
|
||||
"stats-button": "STATYSTYKI",
|
||||
"title": "STATYSTYKI AKTYWNYCH POCIĄGÓW",
|
||||
@@ -556,7 +545,7 @@
|
||||
"no-users": "BRAK AKTYWNYCH GRACZY",
|
||||
"no-spawns": "BRAK OTWARTYCH SPAWNÓW",
|
||||
"no-scenery": "Ups! Ta sceneria nie istnieje!",
|
||||
"return-btn": "POWRÓT DO SCENERII",
|
||||
"return-btn": "POWRÓT DO STRONY GŁÓWNEJ",
|
||||
"history-btn": "Przejdź do widoku historii dyżurnych ruchu",
|
||||
"info-btn": "Wróć do widoku scenerii",
|
||||
"authors-title": "Autor scenerii | Autorzy scenerii",
|
||||
@@ -600,13 +589,10 @@
|
||||
"scope-name": "OGÓLNIE",
|
||||
"scope-hash": "OBECNY HASH",
|
||||
|
||||
"place": "miejsce",
|
||||
"place": "{n}. miejsce",
|
||||
"dispatcher-rating": "Ocena: {n}",
|
||||
"duty-count": "Brak dyżurów | 1 dyżur | Dyżury: {n}",
|
||||
"duration": "Czas:",
|
||||
|
||||
"no-data-general": "Brak zapisanych rekordów scenerii na serwerze PL1!",
|
||||
"no-data-current-hash": "Brak zapisanych rekordów scenerii z obecnym hashem na serwerze PL1!"
|
||||
"duration": "Czas:"
|
||||
}
|
||||
},
|
||||
"availability": {
|
||||
|
||||
@@ -9,8 +9,7 @@ export const tooltipKeys = [
|
||||
'SpawnsTooltip',
|
||||
'UsersTooltip',
|
||||
'HtmlTooltip',
|
||||
'TrainInfoTooltip',
|
||||
'CreatorTooltip'
|
||||
'TrainInfoTooltip'
|
||||
] as const;
|
||||
|
||||
export type TooltipType = (typeof tooltipKeys)[number];
|
||||
|
||||
@@ -85,6 +85,7 @@
|
||||
padding: 0.1em 0.3em;
|
||||
border-radius: 0.2em;
|
||||
font-weight: bold;
|
||||
user-select: none;
|
||||
|
||||
&.twr {
|
||||
background-color: var(--clr-twr);
|
||||
@@ -150,4 +151,4 @@
|
||||
&.active {
|
||||
background-color: lightblue;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -217,19 +217,6 @@ ul {
|
||||
text-shadow: #f050ff 0 0 10px;
|
||||
}
|
||||
|
||||
&--creator {
|
||||
color: var(--clr-primary);
|
||||
color: transparent;
|
||||
|
||||
background: var(--clr-primary);
|
||||
background: linear-gradient(90deg, gold 30%, #ffffff 70%);
|
||||
background-clip: text;
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
|
||||
text-shadow: gold 0 0 10px;
|
||||
}
|
||||
|
||||
&--discord {
|
||||
color: var(--clr-donator);
|
||||
color: transparent;
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
export function isCreator(name: string) {
|
||||
return /(spythere|kowbojyt)/.test(name.toLowerCase());
|
||||
}
|
||||
Reference in New Issue
Block a user