Compare commits
233 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4859a265e7 | |||
| 82d051a7a4 | |||
| c9ad50b01e | |||
| 50f573e8c6 | |||
| f1720c88e8 | |||
| efefebe202 | |||
| 218f616df4 | |||
| 8d737f6429 | |||
| b7abeb4c34 | |||
| 5440b0831d | |||
| fc344684cc | |||
| 8c61e260d4 | |||
| f4f6512969 | |||
| b9b1e438b9 | |||
| 1eb5f3de9e | |||
| 862aebb158 | |||
| 82a016dca7 | |||
| 744804d445 | |||
| 13a9baa175 | |||
| 01bb9f9c2e | |||
| 80b0226a16 | |||
| 7596b7ec2c | |||
| 90e257127c | |||
| 72f28abf10 | |||
| ff6622bc29 | |||
| c9459eb8f4 | |||
| f51886e45a | |||
| 91803d3fef | |||
| 61972c35e6 | |||
| 466ec2084b | |||
| 75b04ad247 | |||
| a3a0fb358c | |||
| b602819179 | |||
| 346d20fc08 | |||
| 56ae593d5e | |||
| 4f6b79c686 | |||
| 4f89b932fb | |||
| 2106675a0a | |||
| 1b68e0174d | |||
| b47f692ac1 | |||
| b6d776d94e | |||
| 3a97f6a7ac | |||
| c03e524f37 | |||
| 89e947d462 | |||
| f286933a81 | |||
| 9bc5f083eb | |||
| a48cc17c08 | |||
| 9098ffbfbc | |||
| 603b55b44f | |||
| ad94c93932 | |||
| e7eb7c4010 | |||
| e2c2d1b99d | |||
| 9756914434 | |||
| 0b397ee31d | |||
| d726c8424e | |||
| 6b0c6d63a4 | |||
| 1723a1b0bd | |||
| 86ba9112de | |||
| cd41e46ef5 | |||
| 32d5f0269b | |||
| 1750e406e0 | |||
| 7457d59dea | |||
| 2578f5c8d4 | |||
| 6af6764c30 | |||
| 50102c6127 | |||
| 210c49fb04 | |||
| bc3db163b1 | |||
| b364586ebc | |||
| f3509ef217 | |||
| d584d99f4c | |||
| 078c941910 | |||
| 76670ceb29 | |||
| 4cdd8ea06a | |||
| 454ae138f7 | |||
| 640c5e262b | |||
| 7416d7d59f | |||
| ca1255b37e | |||
| de0a8520f3 | |||
| e51919896f | |||
| f9cd9a2a33 | |||
| 897d6d0c36 | |||
| b241b60657 | |||
| 1f1bef1cc9 | |||
| 05f8a0ca68 | |||
| 541572e415 | |||
| 61c7f15fcf | |||
| 1da1645c51 | |||
| 8dc670b631 | |||
| 3424f9a952 | |||
| 4079426506 | |||
| 35f2a5ca09 | |||
| b840a6cd46 | |||
| 1aca0f7ed1 | |||
| b009bc03e8 | |||
| baa39a5a99 | |||
| 99cbde3828 | |||
| e72b73ccf0 | |||
| 75e34d9f75 | |||
| 13aa1acc54 | |||
| 966181c977 | |||
| 06eb4bc607 | |||
| c07a16d245 | |||
| 7e67e34526 | |||
| 6cfc535dec | |||
| d072692db7 | |||
| 6255efd98d | |||
| 9c59c30f12 | |||
| 31302cc053 | |||
| 26fd0c67e4 | |||
| d98ec94a66 | |||
| 28485cc28c | |||
| b14c7a9502 | |||
| 90e78e5ac5 | |||
| 27f02e2c2b | |||
| 421ae1db6b | |||
| 90824dc0e5 | |||
| 583c2887e9 | |||
| f915094775 | |||
| d5e735b59e | |||
| 43a724bf13 | |||
| 337425d21c | |||
| 9ff9341851 | |||
| f9276f6c71 | |||
| f4f9a4729f | |||
| ed0906b63e | |||
| 2ae19123a3 | |||
| 0c2be7b927 | |||
| ffce2b572b | |||
| 1b5a26e380 | |||
| c9b681eaee | |||
| 160879adec | |||
| d6ddbe7af7 | |||
| 1c7b72ea1d | |||
| 83f5b07c7e | |||
| 77cb64e25a | |||
| 6d0cc8e7cd | |||
| 7fb4b0ae26 | |||
| ed191d597b | |||
| 5d24accb15 | |||
| 54c6850121 | |||
| fad8c40b4b | |||
| a8485b3531 | |||
| e7dcd125ec | |||
| d886f44c59 | |||
| 5ff6c0504e | |||
| 2158145ae8 | |||
| 6101c9bfb2 | |||
| bd9af9a630 | |||
| 2188dbdf9b | |||
| c4576d7802 | |||
| 86cb4cff4d | |||
| 0534848677 | |||
| c5d116e2db | |||
| 3dce5ec7c0 | |||
| 956ff8afd7 | |||
| 5b629833df | |||
| b53201a7ff | |||
| f0ddd0e27f | |||
| 94bfba2c49 | |||
| 1e4541ef0d | |||
| c25a55a7d9 | |||
| bea3c59405 | |||
| 8cd43adff3 | |||
| 5ab1963117 | |||
| 50d784a0de | |||
| f61135ce6b | |||
| 5e43ece1aa | |||
| 37e4149a34 | |||
| e515203557 | |||
| 0d79c71eba | |||
| 2bbf9a8ac3 | |||
| 45b2bd01a2 | |||
| 665ffb9dce | |||
| 1c2a93fbd5 | |||
| 57ab6cc02d | |||
| aa62240d27 | |||
| a699c9851f | |||
| d7c1f8c5b4 | |||
| c1c01c496a | |||
| a7f92d3ec5 | |||
| ae7be6d6f8 | |||
| bf204d5e36 | |||
| 459876f5b4 | |||
| 4a1a840c16 | |||
| c0552e890c | |||
| 80d6f2b85f | |||
| 17882e3e6e | |||
| 4a03535b07 | |||
| 0a76842e82 | |||
| 0bc2ac1d15 | |||
| 428dd822a2 | |||
| d932ebfa50 | |||
| cbe983f96c | |||
| 7362d4ffbd | |||
| 17266248e3 | |||
| 6cfea4c9b8 | |||
| cb561395ff | |||
| dda67ad993 | |||
| 073288c8a9 | |||
| e532c9f2da | |||
| 1e92c64ae6 | |||
| 407363221b | |||
| 4884b3af2c | |||
| 31745cf4dd | |||
| 956f77cab5 | |||
| 7c3eb12a31 | |||
| 5ba9e95547 | |||
| 79d5413638 | |||
| 105aeddde1 | |||
| 5c840a7525 | |||
| 1fa3d4c3a1 | |||
| 89ceb6ae7f | |||
| 445b799ff5 | |||
| 8678e9393c | |||
| b9a8bacc78 | |||
| 885cb49f2f | |||
| 6eb73ba743 | |||
| fa610f6ee1 | |||
| eff1256265 | |||
| 97e7dc26cc | |||
| 17c0af5696 | |||
| e4d8ec5e9f | |||
| 4d48e88641 | |||
| 27f9403c2d | |||
| 678c40dd9b | |||
| cc06d5cb0a | |||
| 419b01b53f | |||
| d5d008f43f | |||
| f92c47447e | |||
| 65701bee74 | |||
| 76912ceacb | |||
| 839ce80a62 | |||
| 8a87842e0b |
@@ -0,0 +1,13 @@
|
|||||||
|
/* eslint-env node */
|
||||||
|
require('@rushstack/eslint-patch/modern-module-resolution');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
root: true,
|
||||||
|
extends: ['plugin:vue/vue3-essential', 'eslint:recommended', '@vue/eslint-config-typescript', '@vue/eslint-config-prettier/skip-formatting'],
|
||||||
|
rules: {
|
||||||
|
'vue/multi-word-component-names': 'off',
|
||||||
|
},
|
||||||
|
parserOptions: {
|
||||||
|
ecmaVersion: 'latest',
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
root: true,
|
|
||||||
env: {
|
|
||||||
node: true,
|
|
||||||
},
|
|
||||||
extends: [
|
|
||||||
"plugin:vue/vue3-essential",
|
|
||||||
"eslint:recommended",
|
|
||||||
"@vue/typescript/recommended",
|
|
||||||
"@vue/prettier",
|
|
||||||
"@vue/prettier/@typescript-eslint",
|
|
||||||
],
|
|
||||||
parserOptions: {
|
|
||||||
ecmaVersion: 2020,
|
|
||||||
},
|
|
||||||
rules: {
|
|
||||||
"no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
|
|
||||||
"no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
@@ -11,7 +11,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- run: npm ci && npm run build
|
- run: yarn && yarn build
|
||||||
- uses: FirebaseExtended/action-hosting-deploy@v0
|
- uses: FirebaseExtended/action-hosting-deploy@v0
|
||||||
with:
|
with:
|
||||||
repoToken: '${{ secrets.GITHUB_TOKEN }}'
|
repoToken: '${{ secrets.GITHUB_TOKEN }}'
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- run: npm ci && npm run build
|
- run: yarn && yarn build
|
||||||
- uses: FirebaseExtended/action-hosting-deploy@v0
|
- uses: FirebaseExtended/action-hosting-deploy@v0
|
||||||
with:
|
with:
|
||||||
repoToken: '${{ secrets.GITHUB_TOKEN }}'
|
repoToken: '${{ secrets.GITHUB_TOKEN }}'
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [published]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
github-releases-to-discord:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- name: Github Releases To Discord
|
||||||
|
uses: SethCohen/github-releases-to-discord@v1.13.1
|
||||||
|
with:
|
||||||
|
webhook_url: ${{ secrets.WEBHOOK_URL }}
|
||||||
|
color: "15844367"
|
||||||
|
footer_title: "Changelog - Pojazdownik"
|
||||||
|
footer_timestamp: true
|
||||||
@@ -6,6 +6,7 @@ node_modules
|
|||||||
# local env files
|
# local env files
|
||||||
.env.local
|
.env.local
|
||||||
.env.*.local
|
.env.*.local
|
||||||
|
.env
|
||||||
|
|
||||||
# Log files
|
# Log files
|
||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
@@ -21,4 +22,10 @@ pnpm-debug.log*
|
|||||||
*.njsproj
|
*.njsproj
|
||||||
*.sln
|
*.sln
|
||||||
*.sw?
|
*.sw?
|
||||||
node_modules
|
node_modules
|
||||||
|
|
||||||
|
# Lock files
|
||||||
|
*.lock
|
||||||
|
|
||||||
|
# Dev files
|
||||||
|
stockInfoDev.json
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"semi": true,
|
||||||
|
"singleQuote": true,
|
||||||
|
"trailingComma": "es5",
|
||||||
|
"printWidth": 100
|
||||||
|
}
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
presets: ["@vue/cli-plugin-babel/preset"],
|
|
||||||
};
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"hosting": {
|
"hosting": {
|
||||||
"public": "dist",
|
"public": "dist",
|
||||||
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
|
"ignore": [],
|
||||||
"rewrites": [
|
"rewrites": [
|
||||||
{
|
{
|
||||||
"source": "**",
|
"source": "**",
|
||||||
@@ -9,4 +9,4 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<!DOCTYPE html>
|
<!doctype html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
@@ -6,24 +6,50 @@
|
|||||||
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
|
||||||
|
|
||||||
<title>Pojazdownik</title>
|
<title>Pojazdownik</title>
|
||||||
<meta name="description" content="Edytor pociągów online do symulatora Train Driver 2" />
|
<meta name="description" content="Edytor składów online dla symulatora Train Driver 2" />
|
||||||
|
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
|
<link rel="icon" href="/favicon.ico" sizes="any" />
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
|
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
|
<link rel="apple-touch-icon" href="/apple-touch-icon-180x180.png" />
|
||||||
<link rel="manifest" href="/site.webmanifest" />
|
|
||||||
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5" />
|
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5" />
|
||||||
|
<link rel="manifest" href="/site.webmanifest" />
|
||||||
|
|
||||||
<meta name="msapplication-TileColor" content="#da532c" />
|
<meta name="msapplication-TileColor" content="#da532c" />
|
||||||
<meta name="theme-color" content="#e4c428" />
|
<meta name="theme-color" content="#111" />
|
||||||
|
|
||||||
|
<!-- Static OpenGraph meta -->
|
||||||
|
<meta name="description" content="Edytor składów online dla symulatora Train Driver 2" />
|
||||||
|
<meta property="og:url" content="https://pojazdownik-td2.web.app/" />
|
||||||
|
<meta property="og:type" content="website" />
|
||||||
|
<meta property="og:title" content="Pojazdownik" />
|
||||||
|
<meta
|
||||||
|
property="og:description"
|
||||||
|
content="Edytor składów online dla symulatora Train Driver 2"
|
||||||
|
/>
|
||||||
|
<meta
|
||||||
|
property="og:image"
|
||||||
|
content="https://raw.githubusercontent.com/Spythere/api/refs/heads/main/thumbnails/pojazdownik-banner.png"
|
||||||
|
/>
|
||||||
|
<meta property="og:image:width" content="1200" />
|
||||||
|
<meta property="og:image:height" content="630" />
|
||||||
|
<meta property="og:site_name" content="Pojazdownik" />
|
||||||
|
|
||||||
|
<meta name="twitter:card" content="summary_large_image" />
|
||||||
|
<meta name="twitter:title" content="Pojazdownik" />
|
||||||
|
<meta
|
||||||
|
name="twitter:description"
|
||||||
|
content="Edytor składów online dla symulatora Train Driver 2"
|
||||||
|
/>
|
||||||
|
<meta
|
||||||
|
name="twitter:image"
|
||||||
|
content="https://raw.githubusercontent.com/Spythere/api/refs/heads/main/thumbnails/pojazdownik-banner.png"
|
||||||
|
/>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<noscript>
|
<noscript>
|
||||||
<strong>
|
<strong> We're sorry but Pojazdownik doesn't work properly without JavaScript enabled. Please enable it to continue. </strong>
|
||||||
We're sorry but Pojazdownik doesn't work properly without JavaScript enabled. Please enable it to continue.
|
|
||||||
</strong>
|
|
||||||
</noscript>
|
</noscript>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
<script type="module" src="/src/main.ts"></script>
|
<script type="module" src="/src/main.ts"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
|
|||||||
@@ -1,22 +1,39 @@
|
|||||||
{
|
{
|
||||||
"name": "pojazdownik",
|
"name": "pojazdownik",
|
||||||
"version": "1.3.0",
|
"version": "1.9.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"type": "module",
|
||||||
"dev": "vite",
|
"scripts": {
|
||||||
"build": "vue-tsc --noEmit && vite build",
|
"dev": "vite",
|
||||||
"preview": "yarn build && vite preview --port 4174"
|
"build": "vue-tsc --noEmit && vite build",
|
||||||
},
|
"preview": "yarn build && vite preview",
|
||||||
"dependencies": {
|
"type-check": "vue-tsc --noEmit -p tsconfig.app.json --composite false",
|
||||||
"pinia": "^2.0.17",
|
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
|
||||||
"vue": "^3.2.37"
|
"format": "prettier --write src/"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"dependencies": {
|
||||||
"@vitejs/plugin-vue": "^3.2.0",
|
"axios": "^1.4.0",
|
||||||
"sass": "^1.55.0",
|
"lucide-vue-next": "^0.487.0",
|
||||||
"typescript": "^4.8.4",
|
"pinia": "^2.0.17",
|
||||||
"vite": "^3.2.1",
|
"prettier": "^3.0.3",
|
||||||
"vite-plugin-pwa": "^0.13.3",
|
"vue": "^3.2.37",
|
||||||
"vue-tsc": "^1.0.9"
|
"vue-i18n": "9.11.0",
|
||||||
}
|
"vue-router": "4"
|
||||||
}
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@rushstack/eslint-patch": "^1.3.3",
|
||||||
|
"@types/node": "^22.13.9",
|
||||||
|
"@vite-pwa/assets-generator": "^0.2.3",
|
||||||
|
"@vitejs/plugin-vue": "^5.0.3",
|
||||||
|
"@vue/eslint-config-prettier": "^9.0.0",
|
||||||
|
"@vue/eslint-config-typescript": "^12.0.0",
|
||||||
|
"@vue/tsconfig": "^0.5.1",
|
||||||
|
"eslint": "^8.49.0",
|
||||||
|
"eslint-plugin-vue": "^9.17.0",
|
||||||
|
"sass": "^1.59.3",
|
||||||
|
"typescript": "^5.0.2",
|
||||||
|
"vite": "^5.0.12",
|
||||||
|
"vite-plugin-pwa": "^0.17.5",
|
||||||
|
"vue-tsc": "^2.2.8"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
|
After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 1.6 KiB |
@@ -0,0 +1,24 @@
|
|||||||
|
<svg width="500" height="500" viewBox="0 0 500 500" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<mask id="path-1-inside-1_102_63" fill="white">
|
||||||
|
<path d="M0 250C0 111.929 111.929 6.10352e-05 250 6.10352e-05C388.071 6.10352e-05 500 111.929 500 250C500 388.071 388.071 500 250 500C111.929 500 0 388.071 0 250Z"/>
|
||||||
|
</mask>
|
||||||
|
<path d="M0 250C0 111.929 111.929 6.10352e-05 250 6.10352e-05C388.071 6.10352e-05 500 111.929 500 250C500 388.071 388.071 500 250 500C111.929 500 0 388.071 0 250Z" fill="#242424"/>
|
||||||
|
<path d="M0 222.821C0 84.7503 111.929 -27.1785 250 -27.1785C388.071 -27.1785 500 84.7503 500 222.821V250C500 126.939 388.071 27.1787 250 27.1787C111.929 27.1787 0 126.939 0 250V222.821ZM500 277.179C500 415.25 388.071 527.179 250 527.179C111.929 527.179 0 415.25 0 277.179V250C0 373.061 111.929 472.821 250 472.821C388.071 472.821 500 373.061 500 250V277.179ZM0 500V6.10352e-05V500ZM500 6.10352e-05V500V6.10352e-05Z" fill="#FFD600" mask="url(#path-1-inside-1_102_63)"/>
|
||||||
|
<path d="M210.369 301.604C210.369 301.604 210.369 341.807 210.369 364.846C210.369 387.885 202.798 417.491 171.591 417.491C140.385 417.491 132.813 417.491 132.813 417.491L132.812 78.125L250.754 78.125C274.312 78.125 294.504 80.9665 311.331 86.6494C328.311 92.1788 342.232 99.8585 353.093 109.689C364.107 119.519 372.214 131.115 377.415 144.478C382.616 157.84 385.217 172.278 385.217 187.791C385.217 204.533 382.54 219.892 377.186 233.869C371.832 247.846 363.648 259.827 352.634 269.81C341.62 279.794 327.623 287.627 310.643 293.31C293.816 298.839 273.853 301.604 250.754 301.604L210.369 301.604ZM210.369 242.854L250.754 242.854C270.946 242.854 285.479 238.016 294.351 228.34C303.224 218.663 307.66 205.147 307.66 187.791C307.66 180.111 306.512 173.123 304.218 166.825C301.923 160.528 298.405 155.152 293.663 150.698C289.074 146.09 283.184 142.558 275.995 140.1C268.958 137.643 260.544 136.414 250.754 136.414L210.369 136.414L210.369 242.854Z" fill="url(#paint0_linear_102_63)"/>
|
||||||
|
<path d="M239.215 301.604C239.215 301.604 239.215 341.807 239.215 364.846C239.215 387.885 231.643 417.491 200.437 417.491C169.231 417.491 161.659 417.491 161.659 417.491L161.658 78.125L279.6 78.125C303.158 78.125 323.35 80.9665 340.177 86.6494C357.157 92.1788 371.077 99.8585 381.938 109.689C392.952 119.519 401.06 131.115 406.261 144.478C411.462 157.84 414.062 172.278 414.062 187.791C414.062 204.533 411.385 219.892 406.031 233.869C400.677 247.846 392.493 259.827 381.479 269.81C370.465 279.794 356.468 287.627 339.488 293.31C322.662 298.839 302.699 301.604 279.6 301.604L239.215 301.604ZM239.215 242.854L279.6 242.854C299.792 242.854 314.325 238.016 323.197 228.34C332.069 218.663 336.505 205.147 336.505 187.791C336.505 180.111 335.358 173.123 333.064 166.825C330.769 160.528 327.251 155.152 322.509 150.698C317.919 146.09 312.03 142.558 304.84 140.1C297.804 137.643 289.39 136.414 279.6 136.414L239.215 136.414L239.215 242.854Z" fill="url(#paint1_linear_102_63)"/>
|
||||||
|
<path d="M210.685 301.604C210.685 301.604 210.685 341.807 210.685 364.846C210.685 387.885 203.082 417.491 171.749 417.491C140.416 417.491 132.813 417.491 132.813 417.491L132.812 78.125L251.233 78.125C274.887 78.125 295.161 80.9665 312.057 86.6494C329.105 92.1788 343.083 99.8585 353.988 109.689C365.046 119.519 373.187 131.115 378.409 144.478C383.631 157.84 386.242 172.278 386.242 187.791C386.242 204.533 383.555 219.892 378.179 233.869C372.803 247.846 364.586 259.827 353.527 269.81C342.468 279.794 328.414 287.627 311.365 293.31C294.47 298.839 274.426 301.604 251.233 301.604L210.685 301.604ZM210.685 242.854L251.233 242.854C271.508 242.854 286.099 238.016 295.008 228.34C303.916 218.663 308.37 205.147 308.37 187.791C308.37 180.111 307.218 173.123 304.914 166.825C302.611 160.528 299.078 155.152 294.316 150.698C289.709 146.09 283.795 142.558 276.576 140.1C269.511 137.643 261.063 136.414 251.233 136.414L210.685 136.414L210.685 242.854Z" fill="url(#paint2_radial_102_63)"/>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="paint0_linear_102_63" x1="259.015" y1="78.125" x2="259.015" y2="417.491" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop offset="0.135417" stop-color="#FFD600"/>
|
||||||
|
<stop offset="1"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="paint1_linear_102_63" x1="287.86" y1="78.125" x2="287.86" y2="417.491" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop offset="0.135417" stop-color="#FFD600"/>
|
||||||
|
<stop offset="1"/>
|
||||||
|
</linearGradient>
|
||||||
|
<radialGradient id="paint2_radial_102_63" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(259.527 247.808) rotate(0.36307) scale(345.948 325.206)">
|
||||||
|
<stop offset="0.484375" stop-color="white"/>
|
||||||
|
<stop offset="1"/>
|
||||||
|
</radialGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 4.5 KiB |
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 -960 960 960" fill="white" width="48"><path d="M314.696-195.478q-32.507 0-55.862-23.356-23.356-23.355-23.356-55.862v-549.826q0-32.74 23.356-56.262 23.355-23.522 55.862-23.522h429.826q32.74 0 56.262 23.522t23.522 56.262v549.826q0 32.507-23.522 55.862-23.522 23.356-56.262 23.356H314.696Zm0-79.218h429.826v-549.826H314.696v549.826ZM175.478-55.694q-32.74 0-56.262-23.522t-23.522-56.262v-629.044h79.784v629.044h509.044v79.784H175.478Zm139.218-219.002v-549.826 549.826Z"/></svg>
|
||||||
|
After Width: | Height: | Size: 536 B |
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 -960 960 960" fill="white" width="48"><path d="M480-318.087 266.651-531.436l57.131-56.001 116.609 116.609v-343.868h79.218v343.868l116.609-116.609 57.131 56.001L480-318.087ZM225.087-145.869q-32.507 0-55.862-23.356-23.356-23.355-23.356-55.862v-143h79.218v143h509.826v-143h79.783v143q0 32.478-23.521 55.848-23.522 23.37-56.262 23.37H225.087Z"/></svg>
|
||||||
|
After Width: | Height: | Size: 410 B |
@@ -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 |
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 -960 960 960" fill="white" width="48"><path d="M451-107.304q-128.652-10.565-216.892-105.522-88.239-94.956-88.239-225.609 0-78.13 35.5-147.543 35.5-69.413 99.5-114.674l56.566 56.565q-52.609 32.435-82.478 87.957-29.87 55.521-29.87 117.695 0 98.305 64.587 169.609T451-187.087v79.783Zm60 0v-79.783q98.304-11.435 161.609-82.239 63.304-70.804 63.304-169.109 0-103.913-70.978-177.434-70.978-73.522-174.891-76.913h-21.696l62.261 62.826-47.522 47.522-152.783-152.784 152.783-153.349 47.522 47.522-68.479 68.479h22.261q138.522 0 234.914 98.022 96.391 98.022 96.391 236.109 0 130.653-88.022 225.609Q639.652-117.869 511-107.304Z"/></svg>
|
||||||
|
After Width: | Height: | Size: 688 B |
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 -960 960 960" fill="white" width="48"><path d="M575.913-143.782v-72.436h115.956l-173.26-172.825 51.043-51.479 175.261 172.695v-117.391h73v241.436h-242Zm-382.783 2.826L142.652-193l551.217-551.782H575.913v-72.436h242v241.436h-73v-116.391L193.13-140.956Zm198.479-378.522L142.652-768l51.478-52.044 250.088 248.522-52.609 52.044Z"/></svg>
|
||||||
|
After Width: | Height: | Size: 396 B |
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 -960 960 960" fill="white" width="48"><path d="M440.391-318.087v-343.868L323.782-544.782l-57.131-56.566L480-814.696l213.349 213.348-57.131 56.566-116.609-117.173v343.868h-79.218ZM225.087-145.869q-32.507 0-55.862-23.356-23.356-23.355-23.356-55.862v-143h79.218v143h509.826v-143h79.783v143q0 32.478-23.521 55.848-23.522 23.37-56.262 23.37H225.087Z"/></svg>
|
||||||
|
After Width: | Height: | Size: 416 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 |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 26 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 |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 25 KiB |
@@ -5,15 +5,26 @@
|
|||||||
"description": "Generator składów online dla symulatora Train Driver 2",
|
"description": "Generator składów online dla symulatora Train Driver 2",
|
||||||
"icons": [
|
"icons": [
|
||||||
{
|
{
|
||||||
"src": "/android-chrome-192x192.png",
|
"src": "pwa-64x64.png",
|
||||||
|
"sizes": "64x64",
|
||||||
|
"type": "image/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "pwa-192x192.png",
|
||||||
"sizes": "192x192",
|
"sizes": "192x192",
|
||||||
"type": "image/png"
|
"type": "image/png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "/android-chrome-512x512.png",
|
"src": "pwa-512x512.png",
|
||||||
"sizes": "512x512",
|
"sizes": "512x512",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"purpose": "any maskable"
|
"purpose": "any"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "maskable-icon-512x512.png",
|
||||||
|
"sizes": "512x512",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "maskable"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"theme_color": "#2c3149",
|
"theme_color": "#2c3149",
|
||||||
|
|||||||
|
After Width: | Height: | Size: 5.8 KiB |
|
Before Width: | Height: | Size: 9.2 KiB |
|
Before Width: | Height: | Size: 8.9 KiB |
|
Before Width: | Height: | Size: 9.5 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 6.3 KiB |
|
After Width: | Height: | Size: 74 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 8.1 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
@@ -5,19 +5,29 @@
|
|||||||
"description": "Generator składów online dla symulatora Train Driver 2",
|
"description": "Generator składów online dla symulatora Train Driver 2",
|
||||||
"icons": [
|
"icons": [
|
||||||
{
|
{
|
||||||
"src": "/android-chrome-192x192.png",
|
"src": "pwa-64x64.png",
|
||||||
|
"sizes": "64x64",
|
||||||
|
"type": "image/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "pwa-192x192.png",
|
||||||
"sizes": "192x192",
|
"sizes": "192x192",
|
||||||
"type": "image/png"
|
"type": "image/png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "/android-chrome-512x512.png",
|
"src": "pwa-512x512.png",
|
||||||
"sizes": "512x512",
|
"sizes": "512x512",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"purpose": "any maskable"
|
"purpose": "any"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "maskable-icon-512x512.png",
|
||||||
|
"sizes": "512x512",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "maskable"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"theme_color": "#2c3149",
|
"theme_color": "#2c3149",
|
||||||
"background_color": "#2c3149",
|
"background_color": "#2c3149",
|
||||||
"display": "standalone"
|
"display": "standalone"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,181 +1,79 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="image-preview" v-if="store.vehiclePreviewSrc != ''" @click="() => (store.vehiclePreviewSrc = '')">
|
<AppModals />
|
||||||
<img :src="store.vehiclePreviewSrc" alt="preview" />
|
<ImageFullscreenPreview v-if="store.vehiclePreviewSrc" />
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="g-card-dimmer" v-if="store.isRandomizerCardOpen" @click="store.isRandomizerCardOpen = false"></div>
|
<router-view></router-view>
|
||||||
<div class="g-card-dimmer" v-if="store.isRealStockListCardOpen" @click="store.isRealStockListCardOpen = false"></div>
|
|
||||||
|
|
||||||
<keep-alive>
|
|
||||||
<transition name="card-appear">
|
|
||||||
<RealStockCard />
|
|
||||||
</transition>
|
|
||||||
</keep-alive>
|
|
||||||
|
|
||||||
<div class="app_container">
|
|
||||||
<main>
|
|
||||||
<LogoSection />
|
|
||||||
|
|
||||||
<InputsSection />
|
|
||||||
|
|
||||||
<TrainImageSection />
|
|
||||||
|
|
||||||
<StockSection />
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<footer>
|
|
||||||
<div class="text--grayed" style="margin-bottom: 0.25em">
|
|
||||||
Ta strona ma charakter informacyjny. Autor nie ponosi odpowiedzialności za tworzenie pociągów niezgodnych z
|
|
||||||
<a
|
|
||||||
style="color: #ccc"
|
|
||||||
href="https://docs.google.com/document/d/1UAAPUtN0d_RoS4RgOzEzllJZJhA0VcizzCzKW4QylbY/edit"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
regulaminem symulatora Train Driver 2</a
|
|
||||||
>!
|
|
||||||
</div>
|
|
||||||
<div class="text--grayed" style="margin-bottom: 0.25em">Strona jest kompletna dla wersji 2022.2.2 symulatora TD2</div>
|
|
||||||
©
|
|
||||||
<a href="https://td2.info.pl/profile/?u=20777" target="_blank">Spythere</a>
|
|
||||||
{{ new Date().getUTCFullYear() }} | v{{ VERSION }}
|
|
||||||
</footer>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import packageInfo from '.././package.json';
|
|
||||||
|
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
|
|
||||||
import InputsSection from './components/sections/InputsSection.vue';
|
|
||||||
|
|
||||||
import { useStore } from './store';
|
import { useStore } from './store';
|
||||||
import TrainImageSection from './components/sections/TrainImageSection.vue';
|
import ImageFullscreenPreview from './components/utils/ImageFullscreenPreview.vue';
|
||||||
import LogoSection from './components/sections/LogoSection.vue';
|
import AppContainerView from './views/AppContainerView.vue';
|
||||||
import RealStockCard from './components/cards/RealStockCard.vue';
|
import AppModals from './components/app/AppModals.vue';
|
||||||
import StockSection from './components/sections/StockSection.vue';
|
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: { ImageFullscreenPreview, AppContainerView, AppModals },
|
||||||
StockSection,
|
data() {
|
||||||
InputsSection,
|
return { store: useStore() };
|
||||||
TrainImageSection,
|
|
||||||
LogoSection,
|
|
||||||
RealStockCard,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
data: () => ({
|
created() {
|
||||||
VERSION: packageInfo.version,
|
this.loadStockDataFromStorage();
|
||||||
store: useStore(),
|
this.store.setupAPIData();
|
||||||
}),
|
},
|
||||||
|
|
||||||
async created() {
|
computed: {
|
||||||
const stockData = await (
|
currentStockString() {
|
||||||
await fetch(`https://spythere.github.io/api/td2/data/stockInfo.json?t=${Math.floor(Date.now() / 60000)}`)
|
return this.store.stockString;
|
||||||
).json();
|
},
|
||||||
|
},
|
||||||
|
|
||||||
this.store.stockData = stockData;
|
watch: {
|
||||||
|
currentStockString(val: string) {
|
||||||
|
if (val != this.store.chosenStorageStockString) {
|
||||||
|
this.store.chosenStorageStockName = '';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
loadStockDataFromStorage() {
|
||||||
|
const savedData = localStorage.getItem('savedStockData');
|
||||||
|
|
||||||
|
if (!savedData) {
|
||||||
|
localStorage.setItem('savedStockData', JSON.stringify({}));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.store.storageStockData = JSON.parse(savedData);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(
|
||||||
|
'Wystąpił błąd podczas przetwarzania danych o składach z localStorage!',
|
||||||
|
error
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@import './styles/global';
|
|
||||||
|
|
||||||
.app_container {
|
|
||||||
min-height: 100vh;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* APP */
|
/* APP */
|
||||||
#app {
|
#app {
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
|
|
||||||
color: $textColor;
|
color: global.$textColor;
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
padding: 1em 0.5em;
|
padding: 0;
|
||||||
}
|
|
||||||
|
|
||||||
/* HEADER SECTION */
|
@media screen and (max-width: global.$breakpointMd) {
|
||||||
|
|
||||||
h2 {
|
|
||||||
margin: 0;
|
|
||||||
margin-bottom: 0.5em;
|
|
||||||
|
|
||||||
color: $accentColor;
|
|
||||||
font-weight: 700;
|
|
||||||
font-size: 1.2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-bottom {
|
|
||||||
margin: 0;
|
|
||||||
font-size: 1.5em;
|
|
||||||
|
|
||||||
color: #d1d1d1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.image-preview {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
z-index: 99;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
background: rgba(black, 0.85);
|
|
||||||
|
|
||||||
img {
|
|
||||||
width: 90%;
|
|
||||||
max-width: 800px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* MAIN SECTION */
|
|
||||||
|
|
||||||
main {
|
|
||||||
display: grid;
|
|
||||||
gap: 1em 3em;
|
|
||||||
|
|
||||||
width: 100%;
|
|
||||||
max-width: 1300px;
|
|
||||||
min-height: 75vh;
|
|
||||||
|
|
||||||
grid-template-columns: 1fr 2fr;
|
|
||||||
grid-template-rows: auto 360px minmax(400px, 1fr);
|
|
||||||
|
|
||||||
// padding: 0 1em;
|
|
||||||
margin-bottom: 2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* FOOTER SECTION */
|
|
||||||
|
|
||||||
footer {
|
|
||||||
margin-top: auto;
|
|
||||||
text-align: center;
|
|
||||||
padding: 0 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* MOBILE VIEWS */
|
|
||||||
|
|
||||||
@media screen and (max-width: $breakpointMd) {
|
|
||||||
#app {
|
|
||||||
font-size: calc(0.7rem + 0.75vw);
|
font-size: calc(0.7rem + 0.75vw);
|
||||||
}
|
}
|
||||||
|
|
||||||
main {
|
@media screen and (orientation: landscape) and (max-width: global.$breakpointMd) {
|
||||||
display: flex;
|
font-size: calc(0.75rem + 0.4vw);
|
||||||
flex-direction: column;
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
grid-template-rows: 1fr;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<keep-alive>
|
||||||
|
<RealStockCard v-if="store.isRealStockListCardOpen" />
|
||||||
|
</keep-alive>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from 'vue';
|
||||||
|
import { useStore } from '../../store';
|
||||||
|
import RealStockCard from '../cards/RealStockCard.vue';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
components: { RealStockCard },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
store: useStore(),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
<template>
|
||||||
|
<footer>
|
||||||
|
<i18n-t keypath="footer.disclaimer" tag="div" class="text--grayed">
|
||||||
|
<template #tos>
|
||||||
|
<a style="color: #ccc" :href="$t('footer.tos-href')" target="_blank">
|
||||||
|
{{ $t('footer.tos') }}
|
||||||
|
</a>
|
||||||
|
</template>
|
||||||
|
</i18n-t>
|
||||||
|
|
||||||
|
<div class="text--grayed" v-if="store.vehiclesData">
|
||||||
|
{{ $t('footer.version-check', { version: store.compatibleSimulatorVersion }) }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
©
|
||||||
|
<a href="https://td2.info.pl/profile/?u=20777" target="_blank">Spythere</a>
|
||||||
|
{{ new Date().getUTCFullYear() }} | v{{ VERSION }}{{ !isOnProductionHost ? 'dev' : '' }}
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from 'vue';
|
||||||
|
import packageInfo from '../../../package.json';
|
||||||
|
import { useStore } from '../../store';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isOnProductionHost: location.hostname == 'pojazdownik-td2.web.app',
|
||||||
|
VERSION: packageInfo.version,
|
||||||
|
store: useStore(),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
footer {
|
||||||
|
text-align: center;
|
||||||
|
padding: 1em 1em 0 1em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
<template>
|
||||||
|
<main>
|
||||||
|
<LogoSection />
|
||||||
|
<InputsSection />
|
||||||
|
<TrainImageSection />
|
||||||
|
<TabsSection />
|
||||||
|
</main>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from 'vue';
|
||||||
|
import LogoSection from '../sections/LogoSection.vue';
|
||||||
|
import InputsSection from '../sections/InputsSection.vue';
|
||||||
|
import TrainImageSection from '../sections/TrainImageSection.vue';
|
||||||
|
import TabsSection from '../sections/TabsSection.vue';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
components: { LogoSection, InputsSection, TrainImageSection, TabsSection },
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
main {
|
||||||
|
display: grid;
|
||||||
|
gap: 1em;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
max-width: 1350px;
|
||||||
|
|
||||||
|
grid-template-columns: 1fr 2fr;
|
||||||
|
grid-template-rows: auto 360px minmax(300px, 1fr);
|
||||||
|
|
||||||
|
background-color: global.$bgColorDarker;
|
||||||
|
border-radius: 1em;
|
||||||
|
|
||||||
|
min-height: 950px;
|
||||||
|
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: global.$breakpointMd) {
|
||||||
|
main {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,39 +1,93 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="real-stock-card g-card" v-if="store.isRealStockListCardOpen">
|
<div class="real-stock-card g-card" @keydown.esc="store.isRealStockListCardOpen = false">
|
||||||
<div class="g-card_bg" @click="store.isRealStockListCardOpen = false"></div>
|
<div class="g-card_bg" @click="store.isRealStockListCardOpen = false"></div>
|
||||||
|
|
||||||
<div class="card_content">
|
<div class="card_content">
|
||||||
<div class="top-sticky">
|
<div class="card_nav">
|
||||||
<button class="btn btn--text exit-btn" @click="store.isRealStockListCardOpen = false">< POWRÓT</button>
|
<div class="top-pane">
|
||||||
|
|
||||||
<div class="header">
|
|
||||||
<h1>
|
<h1>
|
||||||
REALNE ZESTAWIENIA
|
{{ $t('realstock.title') }}
|
||||||
<div>by <a href="https://td2.info.pl/profile/?u=17708" target="_blank">Railtrains997</a></div>
|
<a href="https://td2.info.pl/profile/?u=17708" target="_blank">Railtrains997</a>
|
||||||
</h1>
|
</h1>
|
||||||
<p>
|
<button class="btn action-exit" @click="store.isRealStockListCardOpen = false">
|
||||||
Pełne informacje o zestawieniach dostępne na stronie
|
<img src="/images/icon-exit.svg" alt="" />
|
||||||
<a href="http://bocznica.eu/files/archiwum/2021r_2021-11-04.html" target="_blank">bocznica.eu</a> (stan na
|
</button>
|
||||||
listopad 2021r.)
|
</div>
|
||||||
</p>
|
|
||||||
|
|
||||||
<input type="text" tabindex="0" v-model="searchedReadyStockName" placeholder="Szukaj zestawienia..." />
|
<div class="filters" ref="focus" tabindex="0">
|
||||||
|
<input
|
||||||
|
list="readyStockDataList"
|
||||||
|
v-model="searchedReadyStockName"
|
||||||
|
:placeholder="$t('realstock.search-name')"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<datalist id="readyStockDataList">
|
||||||
|
<option
|
||||||
|
v-for="stock in filteredCompositionList"
|
||||||
|
:value="stock.stockId"
|
||||||
|
:key="stock.name"
|
||||||
|
>
|
||||||
|
{{ stock.stockId }}
|
||||||
|
</option>
|
||||||
|
</datalist>
|
||||||
|
|
||||||
|
<input
|
||||||
|
list="readyStockStringList"
|
||||||
|
v-model="searchedReadyStockString"
|
||||||
|
:placeholder="$t('realstock.search-stock')"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<datalist id="readyStockStringList">
|
||||||
|
<option
|
||||||
|
v-for="stockType in availableCompositionTypes"
|
||||||
|
:value="stockType"
|
||||||
|
:key="stockType"
|
||||||
|
>
|
||||||
|
{{ stockType }}
|
||||||
|
</option>
|
||||||
|
</datalist>
|
||||||
|
|
||||||
|
<button class="btn action-reset" @click="resetStockFilters">
|
||||||
|
{{ $t('realstock.action-reset') }}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ul v-if="responseStatus == 'loaded'">
|
<ul class="card_list" ref="list" @scroll="onListScroll">
|
||||||
<li
|
<li v-for="rStock in filteredCompositionList" :key="rStock.stockId">
|
||||||
v-for="(stock, key) in computedReadyStockList"
|
<!-- :data-last-selected="store.ch === rStock.stockId" -->
|
||||||
:key="key"
|
<div
|
||||||
tabindex="0"
|
class="stock-title"
|
||||||
@click="choseStock(stock.name, stock.type, stock.number, stock.stockString)"
|
tabindex="0"
|
||||||
@keydown.enter="choseStock(stock.name, stock.type, stock.number, stock.stockString)"
|
@click="chooseStock(rStock)"
|
||||||
>
|
@keydown.enter="chooseStock(rStock)"
|
||||||
<img :src="getIconURL(stock.type)" :alt="stock.type" />
|
>
|
||||||
|
<img class="stock-icon" :src="getIconURL(rStock.type)" :alt="rStock.type" />
|
||||||
|
<b class="text--accent" style="margin-left: 5px"> {{ rStock.name }}</b>
|
||||||
|
<div>{{ rStock.number }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<b class="text--accent"> {{ stock.name }}</b>
|
<div class="stock-thumbnails" ref="thumbnailsRef">
|
||||||
<div>{{ stock.number }}</div>
|
<div
|
||||||
|
class="thumbnail-item"
|
||||||
|
v-for="stockType in rStock.stockString.split(';')"
|
||||||
|
:key="stockType"
|
||||||
|
>
|
||||||
|
<div class="thumbnail-container">
|
||||||
|
<div>{{ stockType }}</div>
|
||||||
|
<img
|
||||||
|
:src="`https://stacjownik.spythere.eu/static/thumbnails/${stockType}.png`"
|
||||||
|
:title="stockType"
|
||||||
|
style="opacity: 0"
|
||||||
|
@error="(e) => onStockItemError(e, stockType)"
|
||||||
|
@load="(e) => ((e.target as HTMLElement).style.opacity = '1')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
<div class="bottom" ref="bottom"></div>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -41,194 +95,276 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import { Vehicle, IReadyStockList } from '../../types';
|
|
||||||
|
|
||||||
import { useStore } from '../../store';
|
import { useStore } from '../../store';
|
||||||
import imageMixin from '../../mixins/imageMixin';
|
import imageMixin from '../../mixins/imageMixin';
|
||||||
import stockMixin from '../../mixins/stockMixin';
|
import stockMixin from '../../mixins/stockMixin';
|
||||||
|
|
||||||
interface ResponseJSONData {
|
import { IRealComposition, VehicleGroupType } from '../../types/common.types';
|
||||||
[key: string]: string;
|
|
||||||
|
function getVehicleType(stockType: string): VehicleGroupType {
|
||||||
|
if (/^E/.test(stockType)) return 'loco-electric';
|
||||||
|
if (/^S/.test(stockType)) return 'loco-diesel';
|
||||||
|
|
||||||
|
return 'wagon-passenger';
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
mixins: [imageMixin, stockMixin],
|
mixins: [imageMixin, stockMixin],
|
||||||
|
|
||||||
setup() {
|
|
||||||
return {
|
|
||||||
store: useStore(),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
data: () => ({
|
data: () => ({
|
||||||
|
store: useStore(),
|
||||||
responseStatus: 'loading',
|
responseStatus: 'loading',
|
||||||
isMobile: 'ontouchstart' in document.documentElement && navigator.userAgent.match(/Mobi/) ? true : false,
|
isMobile:
|
||||||
|
'ontouchstart' in document.documentElement && navigator.userAgent.match(/Mobi/)
|
||||||
|
? true
|
||||||
|
: false,
|
||||||
|
observer: null as IntersectionObserver | null,
|
||||||
searchedReadyStockName: '',
|
searchedReadyStockName: '',
|
||||||
|
searchedReadyStockString: '',
|
||||||
|
visibleIndexesTo: 0,
|
||||||
|
lastSelectedStockId: null as string | null,
|
||||||
|
scrollTop: 0,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.mountObserver();
|
||||||
|
},
|
||||||
|
|
||||||
|
activated() {
|
||||||
|
(this.$refs['focus'] as HTMLElement).focus();
|
||||||
|
|
||||||
|
(this.$refs['list'] as HTMLElement).scrollTo({
|
||||||
|
top: this.scrollTop,
|
||||||
|
behavior: 'auto',
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
computedReadyStockList() {
|
filteredCompositionList() {
|
||||||
if (this.searchedReadyStockName == null) return this.store.readyStockList;
|
return this.store.realCompositionList
|
||||||
|
.filter(
|
||||||
|
(rc) =>
|
||||||
|
rc.stockId
|
||||||
|
.toLocaleLowerCase()
|
||||||
|
.includes(this.searchedReadyStockName.toLocaleLowerCase()) &&
|
||||||
|
rc.stockString
|
||||||
|
.toLocaleLowerCase()
|
||||||
|
.includes(this.searchedReadyStockString.toLocaleLowerCase())
|
||||||
|
)
|
||||||
|
.filter((_, i) => i <= this.visibleIndexesTo);
|
||||||
|
},
|
||||||
|
|
||||||
let filtered: IReadyStockList = {};
|
availableCompositionTypes() {
|
||||||
|
return this.store.realCompositionList
|
||||||
|
.reduce((acc, rs) => {
|
||||||
|
rs.stockString.split(';').forEach((s) => {
|
||||||
|
if (!acc.includes(s)) acc.push(s);
|
||||||
|
});
|
||||||
|
|
||||||
for (let key in this.store.readyStockList) {
|
return acc;
|
||||||
if (key.toLocaleLowerCase().includes(this.searchedReadyStockName.toLocaleLowerCase()))
|
}, [] as string[])
|
||||||
filtered[key] = this.store.readyStockList[key];
|
.sort((a, b) => (a > b ? 1 : -1));
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
filteredCompositionList(curr, prev) {
|
||||||
|
if (curr.length < prev.length) {
|
||||||
|
this.visibleIndexesTo = 20;
|
||||||
|
(this.$refs['list'] as HTMLElement).scrollTo({
|
||||||
|
top: 0,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return filtered;
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
mountObserver() {
|
||||||
|
this.observer = new IntersectionObserver((entries) => {
|
||||||
|
if (entries[0].intersectionRatio > 0) this.visibleIndexesTo += 20;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.observer.observe(this.$refs['bottom'] as HTMLElement);
|
||||||
|
},
|
||||||
|
|
||||||
getImageUrl(name: string) {
|
getImageUrl(name: string) {
|
||||||
return new URL(`./dir/${name}.png`, import.meta.url).href;
|
return new URL(`./dir/${name}.png`, import.meta.url).href;
|
||||||
},
|
},
|
||||||
|
|
||||||
choseStock(name: string, type: string, number: string, stockString: string) {
|
resetStockFilters() {
|
||||||
this.loadStockFromString(stockString);
|
this.searchedReadyStockName = '';
|
||||||
|
this.searchedReadyStockString = '';
|
||||||
|
},
|
||||||
|
|
||||||
|
chooseStock(stockItem: IRealComposition) {
|
||||||
|
this.loadStockFromString(stockItem.stockString);
|
||||||
|
this.lastSelectedStockId = stockItem.stockId;
|
||||||
this.store.isRealStockListCardOpen = false;
|
this.store.isRealStockListCardOpen = false;
|
||||||
},
|
},
|
||||||
},
|
|
||||||
|
|
||||||
async mounted() {
|
onStockItemError(e: Event, stockType: string) {
|
||||||
const readyStockJSONData: ResponseJSONData = await (
|
const imageEl = e.target as HTMLImageElement;
|
||||||
await fetch(`https://spythere.github.io/api/td2/data/readyStock.json?t=${Math.floor(Date.now() / 60000)}`)
|
imageEl.src = `images/${getVehicleType(stockType)}-unknown.png`;
|
||||||
).json();
|
imageEl.style.opacity = '1';
|
||||||
|
},
|
||||||
|
|
||||||
if (!readyStockJSONData) {
|
onListScroll(e: Event) {
|
||||||
this.responseStatus = 'error';
|
const listElement = e.target as HTMLElement;
|
||||||
return;
|
const scrollTop = listElement.scrollTop;
|
||||||
}
|
|
||||||
|
|
||||||
for (let stockKey in readyStockJSONData) {
|
this.scrollTop = scrollTop;
|
||||||
const splittedKey = stockKey.split(' ');
|
},
|
||||||
|
|
||||||
let name = '';
|
|
||||||
for (let i = 2; i < splittedKey.length; i++) {
|
|
||||||
name += ' ' + splittedKey[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
this.store.readyStockList[stockKey] = {
|
|
||||||
type: splittedKey[0],
|
|
||||||
number: splittedKey[1].replace(/_/g, '/'),
|
|
||||||
name,
|
|
||||||
stockString: readyStockJSONData[stockKey],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
this.responseStatus = 'loaded';
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import '../../styles/global.scss';
|
.action-exit {
|
||||||
|
display: flex;
|
||||||
.exit-btn {
|
background-color: #333;
|
||||||
font-size: 1.2em;
|
border-radius: 0.25em;
|
||||||
margin: 0.5em 0;
|
padding: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
input {
|
.action-reset {
|
||||||
width: 100%;
|
background-color: #333;
|
||||||
max-width: 250px;
|
|
||||||
|
|
||||||
&::placeholder {
|
|
||||||
font-size: 0.9em;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.card_content {
|
.card_content {
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: auto 1fr auto;
|
||||||
|
gap: 0.5em;
|
||||||
|
|
||||||
background-color: #1c1c1c;
|
background-color: #1c1c1c;
|
||||||
border-radius: 1em;
|
border-radius: 1em;
|
||||||
|
|
||||||
height: 85vh;
|
height: 95vh;
|
||||||
max-width: 1000px;
|
max-width: 1000px;
|
||||||
width: 90vw;
|
width: 90vw;
|
||||||
|
|
||||||
padding: 0 1em;
|
padding: 0 1em;
|
||||||
|
|
||||||
overflow-y: auto;
|
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
|
|
||||||
.top-sticky {
|
@media screen and (max-width: global.$breakpointSm) {
|
||||||
position: sticky;
|
height: 80vh;
|
||||||
top: 0;
|
}
|
||||||
background: #1c1c1c;
|
}
|
||||||
|
|
||||||
|
.top-pane {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5em;
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
color: #aaa;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-sticky {
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
background: #1c1c1c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5em;
|
||||||
|
|
||||||
|
padding: 0.5em 0;
|
||||||
|
|
||||||
|
input {
|
||||||
|
width: 35%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header {
|
@media screen and (max-width: global.$breakpointSm) {
|
||||||
padding-bottom: 1.5em;
|
flex-wrap: wrap;
|
||||||
padding-top: 0.5em;
|
|
||||||
|
|
||||||
text-align: center;
|
input {
|
||||||
font-size: 1.3em;
|
width: 100%;
|
||||||
|
|
||||||
h1 {
|
|
||||||
line-height: 0.9em;
|
|
||||||
margin: 0.5em 0;
|
|
||||||
|
|
||||||
div {
|
|
||||||
font-size: 0.65em;
|
|
||||||
color: #ccc;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
p {
|
.btn {
|
||||||
margin: 1em 0;
|
width: 100%;
|
||||||
color: #999;
|
|
||||||
font-size: 0.95em;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ul {
|
ul {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5em;
|
||||||
|
|
||||||
|
overflow: auto;
|
||||||
|
|
||||||
|
li {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(4, 1fr);
|
grid-template-columns: 1fr 2fr;
|
||||||
gap: 1em;
|
|
||||||
|
|
||||||
@media screen and (max-width: 700px) {
|
|
||||||
grid-template-columns: repeat(3, 1fr);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 550px) {
|
|
||||||
grid-template-columns: repeat(2, 1fr);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 400px) {
|
|
||||||
grid-template-columns: repeat(1, 1fr);
|
|
||||||
}
|
|
||||||
|
|
||||||
margin-bottom: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul li {
|
|
||||||
padding: 0.5em;
|
|
||||||
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
background: #2b2b2b;
|
background: #2b2b2b;
|
||||||
|
gap: 1rem;
|
||||||
|
padding: 0.1em;
|
||||||
|
|
||||||
img {
|
&[data-last-selected='true'] .stock-title {
|
||||||
height: 0.85em;
|
border: 1px solid global.$accentColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
span {
|
.stock-title {
|
||||||
color: #999;
|
cursor: pointer;
|
||||||
font-weight: bold;
|
padding: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stock-icon {
|
||||||
|
height: 0.85em;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: #222;
|
background: #222;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:focus {
|
@media screen and (max-width: global.$breakpointSm) {
|
||||||
outline: 1px solid white;
|
grid-template-columns: 1fr;
|
||||||
|
// grid-template-rows: 1fr 1fr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
|
||||||
|
|
||||||
|
.stock-thumbnails {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
overflow: auto;
|
||||||
|
padding: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thumbnail-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
div {
|
||||||
|
font-size: 0.9em;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
max-width: 250px;
|
||||||
|
max-height: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > .thumbnail-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom {
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
<template>
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" :data-disabled="disabled" :disabled="disabled" v-model="model" />
|
||||||
|
<div><slot /></div>
|
||||||
|
</label>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
const model = defineModel();
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
disabled: Boolean,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
label {
|
||||||
|
text-transform: uppercase;
|
||||||
|
transition: color 200ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
div {
|
||||||
|
padding: 0.25em 0.5em;
|
||||||
|
color: white;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
background-color: #222;
|
||||||
|
border-radius: 0.25em;
|
||||||
|
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '\2716';
|
||||||
|
margin-right: 0.5em;
|
||||||
|
color: #aaa;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
position: absolute;
|
||||||
|
clip: rect(1px, 1px, 1px, 1px);
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
height: 1px;
|
||||||
|
width: 1px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&:focus-visible + div {
|
||||||
|
outline: 1px solid white;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:checked + div {
|
||||||
|
color: palegreen;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
color: palegreen;
|
||||||
|
content: '\2714';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
|
||||||
|
+ div {
|
||||||
|
opacity: 0.55;
|
||||||
|
cursor: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,17 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<section class="inputs-section">
|
<section class="inputs-section">
|
||||||
<div class="input_container">
|
<div class="input_container">
|
||||||
<h2 class="input_header">WYBIERZ POJAZDY / WAGONY</h2>
|
|
||||||
|
|
||||||
<div class="input_list type">
|
<div class="input_list type">
|
||||||
|
<label for="cargo-select">{{ $t('inputs.vehicles-title') }}</label>
|
||||||
|
|
||||||
<div class="vehicle-types locos">
|
<div class="vehicle-types locos">
|
||||||
<button
|
<button
|
||||||
v-for="locoType in locomotiveTypeList"
|
v-for="locoType in locomotiveTypeList"
|
||||||
|
:key="locoType.id"
|
||||||
class="btn btn--choice"
|
class="btn btn--choice"
|
||||||
:data-selected="locoType.id == store.chosenLocoPower"
|
:data-selected="locoType.id == store.chosenLocoGroup"
|
||||||
@click="selectLocoType(locoType.id)"
|
@click="selectLocoType(locoType.id)"
|
||||||
>
|
>
|
||||||
{{ locoType.value }}
|
{{ $t(`inputs.${locoType.id}`) }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -23,22 +24,28 @@
|
|||||||
@keydown.enter.prevent="addOrSwitchVehicle"
|
@keydown.enter.prevent="addOrSwitchVehicle"
|
||||||
@keydown.backspace="removeVehicle"
|
@keydown.backspace="removeVehicle"
|
||||||
>
|
>
|
||||||
<option :value="null" disabled>Wybierz pojazd trakcyjny</option>
|
<option :value="null" disabled>
|
||||||
|
{{ $t('inputs.input-vehicle') }}
|
||||||
|
</option>
|
||||||
<option v-for="loco in locoOptions" :value="loco" :key="loco.type">
|
<option v-for="loco in locoOptions" :value="loco" :key="loco.type">
|
||||||
{{ loco.type }}
|
{{ loco.type
|
||||||
|
}}<b v-if="loco.sponsorOnlyTimestamp && loco.sponsorOnlyTimestamp > Date.now()">*</b>
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="input_list type">
|
<div class="input_list type">
|
||||||
|
<label for="cargo-select">{{ $t('inputs.carwagons-title') }}</label>
|
||||||
|
|
||||||
<div class="vehicle-types carwagons">
|
<div class="vehicle-types carwagons">
|
||||||
<button
|
<button
|
||||||
v-for="carType in carTypeList"
|
v-for="carType in carTypeList"
|
||||||
|
:key="carType.id"
|
||||||
class="btn btn--choice"
|
class="btn btn--choice"
|
||||||
:data-selected="carType.id == store.chosenCarUseType"
|
:data-selected="carType.id == store.chosenCarGroup"
|
||||||
@click="selectCarWagonType(carType.id)"
|
@click="selectCarWagonType(carType.id)"
|
||||||
>
|
>
|
||||||
{{ carType.value }}
|
{{ $t(`inputs.${carType.id}`) }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -50,21 +57,24 @@
|
|||||||
@keydown.enter.prevent="addOrSwitchVehicle"
|
@keydown.enter.prevent="addOrSwitchVehicle"
|
||||||
@keydown.backspace="removeVehicle"
|
@keydown.backspace="removeVehicle"
|
||||||
>
|
>
|
||||||
<option :value="null" disabled>Wybierz wagon</option>
|
<option :value="null" disabled>
|
||||||
|
{{ $t('inputs.input-carwagon') }}
|
||||||
|
</option>
|
||||||
|
|
||||||
<option v-for="car in carOptions" :value="car" :key="car.type">
|
<option v-for="car in carOptions" :value="car" :key="car.type">
|
||||||
{{ car.type }}
|
{{ car.type
|
||||||
|
}}<b v-if="car.sponsorOnlyTimestamp && car.sponsorOnlyTimestamp > Date.now()">*</b>
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="input_list cargo">
|
<div class="input_list cargo">
|
||||||
<label for="cargo-select">Ładunek (tylko wybrane towarowe)</label>
|
<label for="cargo-select">{{ $t('inputs.cargo-title') }}</label>
|
||||||
<select
|
<select
|
||||||
id="cargo-select"
|
id="cargo-select"
|
||||||
:disabled="
|
:disabled="
|
||||||
(store.chosenCar && !store.chosenCar.loadable) ||
|
(store.chosenCar && !store.chosenCar.loadable) ||
|
||||||
(store.chosenCar && store.chosenCar.useType == 'car-passenger') ||
|
(store.chosenCar && store.chosenCar.group == 'wagon-passenger') ||
|
||||||
!store.chosenCar
|
!store.chosenCar
|
||||||
"
|
"
|
||||||
data-select="cargo"
|
data-select="cargo"
|
||||||
@@ -75,30 +85,37 @@
|
|||||||
@keydown.enter.prevent="addOrSwitchVehicle"
|
@keydown.enter.prevent="addOrSwitchVehicle"
|
||||||
@keydown.backspace="removeVehicle"
|
@keydown.backspace="removeVehicle"
|
||||||
>
|
>
|
||||||
<option :value="null" v-if="!store.chosenCar || !store.chosenCar.loadable">brak dostępnych ładunków</option>
|
<option :value="null" v-if="!store.chosenCar || !store.chosenCar.loadable">
|
||||||
<option :value="null" v-else>próżny</option>
|
{{ $t('inputs.no-cargo-available') }}
|
||||||
|
</option>
|
||||||
|
|
||||||
<option v-for="cargo in store.chosenCar?.cargoList" :value="cargo" :key="cargo.id">
|
<option :value="null" v-else>{{ $t('inputs.cargo-empty') }}</option>
|
||||||
|
|
||||||
|
<option v-for="cargo in store.chosenCar?.cargoTypes" :value="cargo" :key="cargo.id">
|
||||||
{{ cargo.id }}
|
{{ cargo.id }}
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="input_actions">
|
<div class="input_actions">
|
||||||
<button class="btn" @click="addVehicle(store.chosenVehicle, store.chosenCargo)">DODAJ NOWY</button>
|
<button class="btn" @click="addVehicle(store.chosenVehicle, store.chosenCargo)">
|
||||||
|
{{ $t('inputs.action-add') }}
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn"
|
class="btn"
|
||||||
@click="switchVehicles"
|
@click="switchVehicles"
|
||||||
:disabled="store.chosenStockListIndex == -1"
|
:disabled="store.chosenStockListIndex == -1"
|
||||||
:data-disabled="store.chosenStockListIndex == -1"
|
:data-disabled="store.chosenStockListIndex == -1"
|
||||||
>
|
>
|
||||||
ZAMIEŃ ZA
|
{{ $t('inputs.action-swap') }}
|
||||||
<b class="text--accent">
|
<b class="text--accent">
|
||||||
{{ store.chosenStockListIndex == -1 ? '' : `${store.chosenStockListIndex + 1}.` }}
|
{{ store.chosenStockListIndex == -1 ? '' : `${store.chosenStockListIndex + 1}.` }}
|
||||||
</b>
|
</b>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button class="btn" @click="store.isRealStockListCardOpen = true"><b>REALNE ZESTAWIENIA</b></button>
|
<button class="btn" @click="store.isRealStockListCardOpen = true">
|
||||||
|
<b>{{ $t('inputs.real-stock') }}</b>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@@ -107,66 +124,44 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
|
|
||||||
import { IStock } from '../../types';
|
|
||||||
import imageMixin from '../../mixins/imageMixin';
|
import imageMixin from '../../mixins/imageMixin';
|
||||||
import { useStore } from '../../store';
|
import { useStore } from '../../store';
|
||||||
import { isLocomotive } from '../../utils/vehicleUtils';
|
|
||||||
import stockPreviewMixin from '../../mixins/stockPreviewMixin';
|
import stockPreviewMixin from '../../mixins/stockPreviewMixin';
|
||||||
import stockMixin from '../../mixins/stockMixin';
|
import stockMixin from '../../mixins/stockMixin';
|
||||||
|
import { LocoGroupType, WagonGroupType } from '../../types/common.types';
|
||||||
interface ILocoType {
|
|
||||||
id: string;
|
|
||||||
value: string;
|
|
||||||
desc: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
mixins: [imageMixin, stockPreviewMixin, stockMixin],
|
mixins: [imageMixin, stockPreviewMixin, stockMixin],
|
||||||
|
|
||||||
data: () => ({
|
data: () => ({
|
||||||
|
store: useStore(),
|
||||||
locomotiveTypeList: [
|
locomotiveTypeList: [
|
||||||
{
|
{ id: 'loco-electric', desc: 'ELEKTRYCZNE' },
|
||||||
id: 'loco-e',
|
{ id: 'loco-diesel', desc: 'SPALINOWE' },
|
||||||
value: 'ELEKTR',
|
{ id: 'unit-electric', desc: 'ELEKTR. ZESPOŁY TRAKCYJNE' },
|
||||||
desc: 'ELEKTRYCZNE',
|
{ id: 'unit-diesel', desc: 'SPAL. ZESPOŁY TRAKCYJNE' },
|
||||||
},
|
] as { id: LocoGroupType; desc: string }[],
|
||||||
{
|
|
||||||
id: 'loco-s',
|
|
||||||
value: 'SPAL',
|
|
||||||
desc: 'SPALINOWE',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'loco-ezt',
|
|
||||||
value: 'EZT',
|
|
||||||
desc: 'ELEKTR. ZESPOŁY TRAKCYJNE',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'loco-szt',
|
|
||||||
value: 'SZT',
|
|
||||||
desc: 'SPAL. ZESPOŁY TRAKCYJNE',
|
|
||||||
},
|
|
||||||
] as ILocoType[],
|
|
||||||
|
|
||||||
carTypeList: [
|
carTypeList: [
|
||||||
{
|
{ id: 'wagon-passenger', desc: 'PASAŻERSKIE' },
|
||||||
id: 'car-passenger',
|
{ id: 'wagon-freight', desc: 'TOWAROWE' },
|
||||||
value: 'PAS',
|
] as { id: WagonGroupType; desc: string }[],
|
||||||
desc: 'PASAŻERSKIE',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'car-cargo',
|
|
||||||
value: 'TOW',
|
|
||||||
desc: 'TOWAROWE',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
setup() {
|
computed: {
|
||||||
const store = useStore();
|
locoOptions() {
|
||||||
|
return this.store.locoDataList
|
||||||
|
.slice()
|
||||||
|
.sort((a, b) => (a.type > b.type ? 1 : -1))
|
||||||
|
.filter((loco) => loco.group == this.store.chosenLocoGroup);
|
||||||
|
},
|
||||||
|
|
||||||
return {
|
carOptions() {
|
||||||
store,
|
return this.store.carDataList
|
||||||
};
|
.slice()
|
||||||
|
.sort((a, b) => (a.type > b.type ? 1 : -1))
|
||||||
|
.filter((car) => car.group == this.store.chosenCarGroup);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
@@ -177,17 +172,15 @@ export default defineComponent({
|
|||||||
addOrSwitchVehicle() {
|
addOrSwitchVehicle() {
|
||||||
if (!this.store.chosenVehicle) return;
|
if (!this.store.chosenVehicle) return;
|
||||||
|
|
||||||
if (this.store.chosenStockListIndex == -1) this.addVehicle(this.store.chosenVehicle, this.store.chosenCargo);
|
if (this.store.chosenStockListIndex == -1)
|
||||||
|
this.addVehicle(this.store.chosenVehicle, this.store.chosenCargo);
|
||||||
else this.switchVehicles();
|
else this.switchVehicles();
|
||||||
},
|
},
|
||||||
|
|
||||||
removeVehicle() {
|
removeVehicle() {
|
||||||
if (this.store.stockList.length == 0) return;
|
if (this.store.stockList.length == 0) return;
|
||||||
|
|
||||||
const lastStock = this.store.stockList.slice(-1)[0];
|
this.store.stockList.splice(-1);
|
||||||
|
|
||||||
if (lastStock.count > 1) lastStock.count--;
|
|
||||||
else this.store.stockList.splice(-1);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
switchVehicles() {
|
switchVehicles() {
|
||||||
@@ -197,30 +190,40 @@ export default defineComponent({
|
|||||||
|
|
||||||
if (!vehicle) return;
|
if (!vehicle) return;
|
||||||
|
|
||||||
const stockObj: IStock = {
|
const stockObject = this.getStockObject(vehicle, this.store.chosenCargo);
|
||||||
id: `${Date.now()}`,
|
this.store.stockList[this.store.chosenStockListIndex] = stockObject;
|
||||||
useType: isLocomotive(vehicle) ? vehicle.power : vehicle.useType,
|
},
|
||||||
type: vehicle.type,
|
|
||||||
length: vehicle.length,
|
|
||||||
mass: vehicle.mass,
|
|
||||||
maxSpeed: vehicle.maxSpeed,
|
|
||||||
isLoco: isLocomotive(vehicle),
|
|
||||||
cargo:
|
|
||||||
!isLocomotive(vehicle) && vehicle.loadable && this.store.chosenCargo ? this.store.chosenCargo : undefined,
|
|
||||||
count: 1,
|
|
||||||
imgSrc: vehicle.imageSrc,
|
|
||||||
supportersOnly: vehicle.supportersOnly,
|
|
||||||
};
|
|
||||||
|
|
||||||
this.store.stockList[this.store.chosenStockListIndex] = stockObj;
|
selectLocoType(locoGroupType: LocoGroupType) {
|
||||||
|
this.store.chosenLocoGroup = locoGroupType;
|
||||||
|
this.store.chosenVehicle = this.locoOptions[0];
|
||||||
|
this.store.chosenLoco = this.locoOptions[0];
|
||||||
|
},
|
||||||
|
|
||||||
|
selectCarWagonType(wagonGroupType: WagonGroupType) {
|
||||||
|
this.store.chosenCarGroup = wagonGroupType;
|
||||||
|
this.store.chosenVehicle = this.carOptions[0];
|
||||||
|
this.store.chosenCar = this.carOptions[0];
|
||||||
|
this.store.chosenCargo = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
previewVehicleByType(type: 'loco' | 'car' | 'cargo') {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if (!this.store.chosenLoco && !this.store.chosenCar) return;
|
||||||
|
|
||||||
|
this.store.chosenVehicle = type == 'loco' ? this.store.chosenLoco : this.store.chosenCar;
|
||||||
|
|
||||||
|
this.store.chosenCargo =
|
||||||
|
this.store.chosenCar?.cargoTypes.find(
|
||||||
|
(cargo) => cargo.id == this.store.chosenCargo?.id
|
||||||
|
) || null;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import '../../styles/global';
|
|
||||||
|
|
||||||
.inputs-section {
|
.inputs-section {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@@ -229,8 +232,9 @@ export default defineComponent({
|
|||||||
grid-column: 1;
|
grid-column: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input_header {
|
.input_container {
|
||||||
margin-bottom: 1em;
|
width: 100%;
|
||||||
|
max-width: 380px;
|
||||||
}
|
}
|
||||||
|
|
||||||
button.btn--choice {
|
button.btn--choice {
|
||||||
@@ -238,7 +242,7 @@ button.btn--choice {
|
|||||||
padding: 0.3em 0.6em;
|
padding: 0.3em 0.6em;
|
||||||
|
|
||||||
&[data-selected='true'] {
|
&[data-selected='true'] {
|
||||||
background-color: $accentColor;
|
background-color: global.$accentColor;
|
||||||
color: black;
|
color: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -248,16 +252,20 @@ button.btn--choice {
|
|||||||
.input_list {
|
.input_list {
|
||||||
margin: 0.5em 0;
|
margin: 0.5em 0;
|
||||||
|
|
||||||
|
select {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
label {
|
label {
|
||||||
display: block;
|
display: block;
|
||||||
|
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: $accentColor;
|
color: global.$accentColor;
|
||||||
margin-bottom: 0.3em;
|
margin-bottom: 0.3em;
|
||||||
}
|
}
|
||||||
|
|
||||||
select:focus {
|
select:focus {
|
||||||
border-color: $accentColor;
|
border-color: global.$accentColor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -276,9 +284,13 @@ button.btn--choice {
|
|||||||
gap: 0.25em;
|
gap: 0.25em;
|
||||||
|
|
||||||
margin-bottom: 0.5em;
|
margin-bottom: 0.5em;
|
||||||
|
|
||||||
|
button {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: $breakpointMd) {
|
@media screen and (max-width: global.$breakpointMd) {
|
||||||
.inputs-section {
|
.inputs-section {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@@ -289,4 +301,3 @@ button.btn--choice {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,40 @@
|
|||||||
<template>
|
<template>
|
||||||
<section class="logo-section">
|
<section class="logo-section">
|
||||||
<img src="/images/logo.svg" alt="logo pojazdownik" />
|
<img :src="`/logo-${$i18n.locale}.svg`" alt="logo pojazdownik" @click="navigate" />
|
||||||
|
|
||||||
|
<div class="actions">
|
||||||
|
<button
|
||||||
|
v-for="action in localeActions"
|
||||||
|
:key="action.name"
|
||||||
|
class="btn btn--text"
|
||||||
|
:data-selected="$i18n.locale == action.locale"
|
||||||
|
@click="chooseLocale(action.locale)"
|
||||||
|
>
|
||||||
|
{{ action.name }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export default {
|
export default {
|
||||||
setup() {
|
data() {
|
||||||
return {};
|
return {
|
||||||
|
localeActions: [
|
||||||
|
{ name: 'POLSKI', locale: 'pl' },
|
||||||
|
{ name: 'ENGLISH', locale: 'en' },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
navigate() {
|
||||||
|
window.location.pathname = '';
|
||||||
|
},
|
||||||
|
|
||||||
|
chooseLocale(locale: string) {
|
||||||
|
this.$i18n.locale = locale;
|
||||||
|
window.localStorage.setItem('locale', locale);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
@@ -16,16 +43,28 @@ export default {
|
|||||||
.logo-section {
|
.logo-section {
|
||||||
grid-row: 1;
|
grid-row: 1;
|
||||||
grid-column: 1;
|
grid-column: 1;
|
||||||
margin-bottom: 1.5em;
|
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5em;
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5em;
|
||||||
|
|
||||||
|
button[data-selected='true'] {
|
||||||
|
font-weight: bold;
|
||||||
|
color: global.$accentColor;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
img {
|
img {
|
||||||
max-width: 25em;
|
max-width: 25em;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,114 +0,0 @@
|
|||||||
<template>
|
|
||||||
<section class="stock-section">
|
|
||||||
<div class="section_modes">
|
|
||||||
<button
|
|
||||||
class="btn"
|
|
||||||
v-for="(id, name) in sectionModes"
|
|
||||||
@click="chooseSection(id)"
|
|
||||||
:data-selected="store.stockSectionMode == id"
|
|
||||||
>
|
|
||||||
{{ name }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<transition name="tab-change" mode="out-in">
|
|
||||||
<keep-alive>
|
|
||||||
<component :is="chosenSectionComponent" :key="chosenSectionComponent"></component>
|
|
||||||
</keep-alive>
|
|
||||||
</transition>
|
|
||||||
</section>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { computed, KeepAlive } from 'vue';
|
|
||||||
import { useStore } from '../../store';
|
|
||||||
import StockListTab from '../tabs/StockListTab.vue';
|
|
||||||
import StockGeneratorTab from '../tabs/StockGeneratorTab.vue';
|
|
||||||
import NumberGeneratorTab from '../tabs/NumberGeneratorTab.vue';
|
|
||||||
|
|
||||||
const store = useStore();
|
|
||||||
type SectionMode = typeof store.stockSectionMode;
|
|
||||||
|
|
||||||
const sectionModes: { [key: string]: SectionMode } = {
|
|
||||||
SKŁAD: 'stock-list',
|
|
||||||
'GNR. NUMERU': 'number-generator',
|
|
||||||
'GNR. SKŁADU': 'stock-generator',
|
|
||||||
};
|
|
||||||
|
|
||||||
const chosenSectionComponent = computed(() => {
|
|
||||||
switch (store.stockSectionMode) {
|
|
||||||
case 'stock-list':
|
|
||||||
return StockListTab;
|
|
||||||
|
|
||||||
case 'stock-generator':
|
|
||||||
return StockGeneratorTab;
|
|
||||||
|
|
||||||
case 'number-generator':
|
|
||||||
return NumberGeneratorTab;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return StockListTab;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function chooseSection(sectionId: SectionMode) {
|
|
||||||
store.stockSectionMode = sectionId;
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
@import '../../styles/global.scss';
|
|
||||||
|
|
||||||
// Tab change animation
|
|
||||||
.tab-change {
|
|
||||||
&-enter-from,
|
|
||||||
&-leave-to {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-enter-active,
|
|
||||||
&-leave-active {
|
|
||||||
transition: all 100ms ease-in-out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Section styles
|
|
||||||
.stock-section {
|
|
||||||
grid-row: 1 / 4;
|
|
||||||
grid-column: 2;
|
|
||||||
|
|
||||||
overflow: hidden;
|
|
||||||
padding: 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.section_modes {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(3, 1fr);
|
|
||||||
gap: 0.5em;
|
|
||||||
|
|
||||||
margin-bottom: 0.5em;
|
|
||||||
|
|
||||||
button {
|
|
||||||
position: relative;
|
|
||||||
border-radius: 0.5em 0.5em 0 0;
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
left: 50%;
|
|
||||||
transform: translateX(-50%);
|
|
||||||
|
|
||||||
content: '';
|
|
||||||
width: 0;
|
|
||||||
height: 2px;
|
|
||||||
transition: all 100ms;
|
|
||||||
background-color: $accentColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
&[data-selected='true']::after {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
<template>
|
||||||
|
<section class="tabs-section">
|
||||||
|
<div class="tabs-modes">
|
||||||
|
<router-link
|
||||||
|
v-for="(route, i) in routes"
|
||||||
|
:key="route.name"
|
||||||
|
class="link-btn"
|
||||||
|
:to="route.href"
|
||||||
|
:style="{ 'grid-area': route.name }"
|
||||||
|
>
|
||||||
|
<span class="text--accent">{{ i + 1 }}.</span> {{ $t(`topbar.${route.name}`) }}
|
||||||
|
<span class="text--grayed" v-if="route.name == 'stock'"
|
||||||
|
>({{ store.stockList.length }})</span
|
||||||
|
>
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<transition name="tab-change" mode="out-in">
|
||||||
|
<keep-alive>
|
||||||
|
<component :is="route.meta.viewMode"></component>
|
||||||
|
</keep-alive>
|
||||||
|
</transition>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { onMounted } from 'vue';
|
||||||
|
import { useStore } from '../../store';
|
||||||
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
const store = useStore();
|
||||||
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const routes = [
|
||||||
|
{
|
||||||
|
name: 'stock',
|
||||||
|
href: '/',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'wiki',
|
||||||
|
href: '/wiki',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'storage',
|
||||||
|
href: '/storage',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'numgen',
|
||||||
|
href: '/numgen',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'stockgen',
|
||||||
|
href: '/stockgen',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
window.addEventListener('keydown', (e) => {
|
||||||
|
if (e.target instanceof HTMLInputElement) return;
|
||||||
|
|
||||||
|
if (/^[12345]$/.test(e.key)) {
|
||||||
|
const keyNum = Number(e.key);
|
||||||
|
|
||||||
|
router.push(routes[keyNum - 1].href);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
// Tab change animation
|
||||||
|
.tab-change {
|
||||||
|
&-enter-from,
|
||||||
|
&-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-enter-active,
|
||||||
|
&-leave-active {
|
||||||
|
transition: all 100ms ease-in-out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Section styles
|
||||||
|
.tabs-section {
|
||||||
|
grid-row: 1 / 4;
|
||||||
|
grid-column: 2;
|
||||||
|
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs-modes {
|
||||||
|
display: grid;
|
||||||
|
grid-template-areas:
|
||||||
|
'stock stock wiki wiki storage storage'
|
||||||
|
'numgen numgen numgen stockgen stockgen stockgen';
|
||||||
|
grid-template-columns: repeat(6, 1fr);
|
||||||
|
// grid-template-rows: 1fr 1fr;
|
||||||
|
padding: 1px;
|
||||||
|
|
||||||
|
gap: 0.5em;
|
||||||
|
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: global.$breakpointSm) {
|
||||||
|
.tabs-modes {
|
||||||
|
grid-template-areas:
|
||||||
|
'stock wiki'
|
||||||
|
'storage storage'
|
||||||
|
'numgen stockgen';
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,53 +1,76 @@
|
|||||||
<template>
|
<template>
|
||||||
<section class="train-image-section">
|
<section class="train-image-section">
|
||||||
<div class="train-image__wrapper">
|
<div v-if="store.chosenVehicle">
|
||||||
<div class="train-image__content">
|
<div class="image-wrapper">
|
||||||
<transition name="img-message-anim">
|
|
||||||
<div class="empty-message" v-if="store.imageLoading && store.chosenVehicle?.imageSrc">
|
|
||||||
ŁADOWANIE OBRAZU...
|
|
||||||
</div>
|
|
||||||
</transition>
|
|
||||||
|
|
||||||
<div class="no-img" v-if="!store.chosenVehicle">PODGLĄD WYBRANEGO POJAZDU</div>
|
|
||||||
|
|
||||||
<img
|
<img
|
||||||
v-if="store.chosenVehicle"
|
:src="getThumbnailURL(store.chosenVehicle.type, 'small')"
|
||||||
:src="`https://spythere.github.io/api/td2/images/${store.chosenVehicle.type}--300px.jpg`"
|
:data-preview-available="isDataPreviewAvailable"
|
||||||
:alt="store.chosenVehicle.type"
|
:data-sponsor-only="
|
||||||
@load="onImageLoad"
|
store.chosenVehicle.sponsorOnlyTimestamp &&
|
||||||
|
store.chosenVehicle.sponsorOnlyTimestamp > Date.now()
|
||||||
|
"
|
||||||
|
:data-team-only="store.chosenVehicle.teamOnly"
|
||||||
@click="onImageClick"
|
@click="onImageClick"
|
||||||
|
@keydown.enter="onImageClick"
|
||||||
|
@load="onImageLoad"
|
||||||
|
@error="onImageError"
|
||||||
|
tabindex="0"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- <div class="empty-message" v-if="store.chosenVehicle && !store.chosenVehicle.imageSrc">Ten pojazd nie ma jeszcze podglądu!</div> -->
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="train-image__info" v-if="store.chosenVehicle">
|
<div class="image-info">
|
||||||
<b class="text--accent">{{ store.chosenVehicle.type }}</b> •
|
<b class="text--accent">{{ store.chosenVehicle.type }}</b> •
|
||||||
<b style="color: #ccc">{{
|
<b style="color: #ccc">
|
||||||
vehicleTypes[
|
{{
|
||||||
isLocomotive(store.chosenVehicle) ? store.chosenVehicle.power : store.chosenVehicle.useType || 'loco-e'
|
$t(
|
||||||
]
|
`preview.${isTractionUnit(store.chosenVehicle) ? store.chosenVehicle.group : store.chosenVehicle.group}`
|
||||||
}}</b>
|
)
|
||||||
|
}}
|
||||||
|
</b>
|
||||||
|
|
||||||
<div style="color: #ccc">
|
<div style="color: #ccc">
|
||||||
<div>
|
<div>
|
||||||
{{ store.chosenVehicle.length }}m | {{ store.chosenVehicle.mass }}t |
|
{{ store.chosenVehicle.length }}m |
|
||||||
|
{{ (store.chosenVehicle.weight / 1000).toFixed(1) }}t |
|
||||||
{{ store.chosenVehicle.maxSpeed }} km/h
|
{{ store.chosenVehicle.maxSpeed }} km/h
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="isLocomotive(store.chosenVehicle)">Typ kabiny: {{ store.chosenVehicle.cabinType }}</div>
|
<div v-if="isTractionUnit(store.chosenVehicle)">
|
||||||
|
{{ $t('preview.cabin') }} {{ store.chosenVehicle.cabinType }}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div v-else>
|
<div v-else>
|
||||||
{{
|
{{
|
||||||
store.chosenVehicle.useType == 'car-cargo'
|
store.chosenVehicle.group == 'wagon-freight'
|
||||||
? store.stockData?.usage[store.chosenVehicle.constructionType]
|
? $t(`usage.${store.chosenVehicle.constructionType}`)
|
||||||
: 'Typ konstrukcji: ' + store.chosenVehicle.constructionType
|
: `${$t('preview.construction')} ${store.chosenVehicle.constructionType}`
|
||||||
}}
|
}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<b
|
||||||
|
v-if="
|
||||||
|
store.chosenVehicle.sponsorOnlyTimestamp &&
|
||||||
|
store.chosenVehicle.sponsorOnlyTimestamp > Date.now()
|
||||||
|
"
|
||||||
|
class="sponsor-only"
|
||||||
|
>
|
||||||
|
{{
|
||||||
|
$t('preview.sponsor-only', [
|
||||||
|
new Date(store.chosenVehicle.sponsorOnlyTimestamp).toLocaleDateString(
|
||||||
|
$i18n.locale == 'pl' ? 'pl-PL' : 'en-GB'
|
||||||
|
),
|
||||||
|
])
|
||||||
|
}}
|
||||||
|
</b>
|
||||||
|
|
||||||
|
<b v-if="store.chosenVehicle.teamOnly" class="team-only">{{ $t('preview.team-only') }}</b>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="train-image__info" v-else>Wybierz pojazd lub wagon, aby zobaczyć jego podgląd powyżej</div>
|
<div v-else>
|
||||||
|
<img src="/images/placeholder.jpg" alt="placeholder image" />
|
||||||
|
<div class="image-info">{{ $t('preview.desc') }}</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
@@ -55,143 +78,165 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, defineComponent } from 'vue';
|
import { computed, defineComponent } from 'vue';
|
||||||
import { useStore } from '../../store';
|
import { useStore } from '../../store';
|
||||||
import { isLocomotive } from '../../utils/vehicleUtils';
|
import { isTractionUnit } from '../../utils/vehicleUtils';
|
||||||
import { ILocomotive, Vehicle } from '../../types';
|
import imageMixin from '../../mixins/imageMixin';
|
||||||
|
import { watch } from 'vue';
|
||||||
|
import { storeToRefs } from 'pinia';
|
||||||
|
import { Ref } from 'vue';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
mixins: [imageMixin],
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
noImageAvailable: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
setup() {
|
setup() {
|
||||||
const store = useStore();
|
const store = useStore();
|
||||||
|
const { chosenVehicle } = storeToRefs(store);
|
||||||
|
|
||||||
|
const imageStatus = ref('none') as Ref<'none' | 'loading' | 'loaded' | 'error'>;
|
||||||
|
|
||||||
|
watch(chosenVehicle, () => {
|
||||||
|
imageStatus.value = 'loading';
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
store,
|
store,
|
||||||
|
imageStatus,
|
||||||
chosenVehicle: computed(() => store.chosenVehicle),
|
chosenVehicle: computed(() => store.chosenVehicle),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
computed: {
|
||||||
return {
|
isDataPreviewAvailable() {
|
||||||
vehicleTypes: {
|
return this.imageStatus == 'loaded' && this.store.chosenVehicle != null;
|
||||||
'loco-e': 'ELEKTROWÓZ',
|
|
||||||
'loco-s': 'SPALINOWÓZ',
|
|
||||||
'loco-ezt': 'EZT',
|
|
||||||
'loco-szt': 'SZT',
|
|
||||||
'car-passenger': 'WAGON PASAŻERSKI',
|
|
||||||
'car-cargo': 'WAGON TOWAROWY',
|
|
||||||
} as { [key: string]: string },
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
|
||||||
chosenVehicle(vehicle: Vehicle, prevVehicle: Vehicle) {
|
|
||||||
if (vehicle && vehicle.type != prevVehicle?.type) {
|
|
||||||
this.store.imageLoading = true;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
onImageLoad() {
|
isTractionUnit,
|
||||||
this.store.imageLoading = false;
|
|
||||||
|
onImageError(e: Event) {
|
||||||
|
const el = e.target as HTMLImageElement;
|
||||||
|
if (el.src == '/images/no-vehicle-image.png') return;
|
||||||
|
|
||||||
|
el.src = '/images/no-vehicle-image.png';
|
||||||
|
this.imageStatus = 'error';
|
||||||
},
|
},
|
||||||
|
|
||||||
isLocomotive(vehicle: Vehicle): vehicle is ILocomotive {
|
onImageLoad(e: Event) {
|
||||||
return isLocomotive(vehicle);
|
if (this.imageStatus == 'error') return;
|
||||||
|
|
||||||
|
this.imageStatus = 'loaded';
|
||||||
},
|
},
|
||||||
|
|
||||||
onImageClick() {
|
onImageClick(e: Event) {
|
||||||
|
const target = e.target as HTMLImageElement;
|
||||||
|
|
||||||
const chosenVehicle = this.store.chosenVehicle;
|
const chosenVehicle = this.store.chosenVehicle;
|
||||||
|
|
||||||
if (!chosenVehicle) return;
|
if (!chosenVehicle) return;
|
||||||
|
|
||||||
this.store.vehiclePreviewSrc = `https://spythere.github.io/api/td2/images/${chosenVehicle.type}--800px.jpg`;
|
this.store.lastFocusedElement = target;
|
||||||
|
|
||||||
|
if (this.isDataPreviewAvailable) {
|
||||||
|
this.store.vehiclePreviewSrc = this.getThumbnailURL(chosenVehicle.type, 'large');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import '../../styles/global.scss';
|
|
||||||
|
|
||||||
.train-image-section {
|
.train-image-section {
|
||||||
grid-row: 3;
|
display: flex;
|
||||||
grid-column: 1;
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
margin-top: 2em;
|
min-height: 250px;
|
||||||
height: 22em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.train-image {
|
& > div {
|
||||||
&__wrapper {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__content {
|
|
||||||
border: 1px solid white;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
max-width: 100%;
|
||||||
|
width: 380px;
|
||||||
max-width: 22em;
|
|
||||||
height: 13em;
|
|
||||||
margin: 0 auto;
|
|
||||||
|
|
||||||
&.supporter {
|
|
||||||
border: 1px solid salmon;
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.empty-message,
|
|
||||||
.no-img {
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
bottom: 0;
|
|
||||||
|
|
||||||
padding: 0.3em 0;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.empty-message {
|
|
||||||
background: rgba(#000, 0.75);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.train-image__info {
|
img {
|
||||||
margin: 1em 0;
|
width: 100%;
|
||||||
|
|
||||||
|
&[data-preview-available='true'] {
|
||||||
|
border: 1px solid white;
|
||||||
|
cursor: zoom-in;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-sponsor-only='true'] {
|
||||||
|
border: 1px solid global.$sponsorColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-team-only='true'] {
|
||||||
|
border: 1px solid global.$teamColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-wrapper {
|
||||||
|
position: relative;
|
||||||
|
min-height: 220px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-placeholder {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
background-color: #2b2b2b;
|
||||||
|
padding: 1em;
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-info {
|
||||||
font-size: 1.1em;
|
font-size: 1.1em;
|
||||||
padding: 0 1em;
|
padding: 0.5em;
|
||||||
|
// margin: 0.5em auto;
|
||||||
|
line-height: 1.35;
|
||||||
|
|
||||||
b {
|
width: 100%;
|
||||||
font-size: 1.1em;
|
max-width: 380px;
|
||||||
}
|
|
||||||
|
|
||||||
div {
|
background-color: global.$secondaryColor;
|
||||||
margin: 0.25em 0;
|
font-weight: bold;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transition animations
|
.placeholder {
|
||||||
.img-message-anim {
|
height: 250px;
|
||||||
&-enter-from,
|
|
||||||
&-leave-to {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-enter-active,
|
background-color: global.$bgColor;
|
||||||
&-leave-active {
|
|
||||||
transition: opacity 75ms ease-in 100ms;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: $breakpointMd) {
|
.sponsor-only {
|
||||||
|
color: global.$sponsorColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
.team-only {
|
||||||
|
color: global.$teamColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: global.$breakpointMd) {
|
||||||
.train-image-section {
|
.train-image-section {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,36 +1,132 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="number-generator tab">
|
<div class="number-generator tab">
|
||||||
<div class="tab_header">
|
<div class="tab_header">
|
||||||
<h2>GENERATOR NUMERU POCIĄGU</h2>
|
<h2>{{ $t('numgen.title') }}</h2>
|
||||||
|
<h3>{{ $t('numgen.subtitle') }}</h3>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tab_content">
|
<div class="tab_content">
|
||||||
<div class="options">
|
<div class="inputs">
|
||||||
<select v-model="beginRegionName" @change="randomizeTrainNumber">
|
<label>
|
||||||
<option :value="null" disabled>Początkowy obszar konstrukcyjny</option>
|
<span>{{ $t('numgen.train-category') }}</span>
|
||||||
<option v-for="(_, name) in genData.regionNumbers" :value="name">{{ name }}</option>
|
<select v-model="chosenCategory" @change="randomizeTrainNumber()">
|
||||||
</select>
|
<option :value="null" disabled>
|
||||||
|
{{ $t('numgen.train-category') }}
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
v-for="(_, category) in genData.categoriesRules"
|
||||||
|
:key="category"
|
||||||
|
:value="category"
|
||||||
|
>
|
||||||
|
{{ $t(`numgen.categories.${category}`) }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
<select v-model="endRegionName" @change="randomizeTrainNumber">
|
<label>
|
||||||
<option :value="null" disabled>Końcowy obszar konstrukcyjny</option>
|
<span>{{ $t('numgen.start-region') }}</span>
|
||||||
<option v-for="(_, name) in genData.regionNumbers" :value="name">{{ name }}</option>
|
<select v-model="beginRegionName" @change="randomizeTrainNumber()">
|
||||||
</select>
|
<option :value="null" disabled>
|
||||||
|
{{ $t('numgen.start-region') }}
|
||||||
|
</option>
|
||||||
|
<option v-for="(_, name) in genData.regionNumbers" :key="name" :value="name">
|
||||||
|
{{ name }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
<select v-model="categoryRules" @change="randomizeTrainNumber">
|
<label>
|
||||||
<option :value="null" disabled>Kategoria pociągu</option>
|
<span> {{ $t('numgen.end-region') }}</span>
|
||||||
<option v-for="(rules, category) in genData.categories" :value="rules">{{ category }}</option>
|
<select v-model="endRegionName" @change="randomizeTrainNumber()">
|
||||||
</select>
|
<option :value="null" disabled>
|
||||||
|
{{ $t('numgen.end-region') }}
|
||||||
|
</option>
|
||||||
|
<option v-for="(_, name) in genData.regionNumbers" :key="name" :value="name">
|
||||||
|
{{ name }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="generated-number">
|
<div class="generated-number" @click="copyNumber">
|
||||||
<span v-if="trainNumber">Wygenerowany numer pociągu: <b class="text--accent">{{ trainNumber }}</b></span>
|
<span v-if="trainNumber">
|
||||||
<span v-else>Wybierz obszary konstrukcyjne i kategorię!</span>
|
{{ $t('numgen.number-info') }}
|
||||||
|
<b class="text--accent">{{ trainNumber }}</b>
|
||||||
|
</span>
|
||||||
|
<span v-else>{{ $t('numgen.warning') }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr>
|
<div class="category-rules" v-if="chosenCategory && categoryRules && trainNumber">
|
||||||
|
<!-- First & second digit (the same regions) -->
|
||||||
|
<div v-if="beginRegionName && endRegionName && beginRegionName == endRegionName">
|
||||||
|
<b>{{ $t('numgen.rules.two-first-digits') }}</b>
|
||||||
|
{{ $t('numgen.rules.from-pool') }}
|
||||||
|
<b class="text--accent">{{ genData.sameRegions[beginRegionName].join(', ') }}</b>
|
||||||
|
{{ $t('numgen.rules.for-region') }} {{ beginRegionName }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- First & second digit (different regions) -->
|
||||||
|
<div v-else>
|
||||||
|
<div>
|
||||||
|
<b>
|
||||||
|
{{ $t('numgen.rules.first-digit') }}
|
||||||
|
<span class="text--accent">{{ trainNumber[0] }}</span>
|
||||||
|
</b>
|
||||||
|
{{ $t('numgen.rules.for-region-begin') }} {{ beginRegionName }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<b>
|
||||||
|
{{ $t('numgen.rules.second-digit') }}
|
||||||
|
<span class="text--accent">{{ trainNumber[1] }} </span>
|
||||||
|
</b>
|
||||||
|
{{ $t('numgen.rules.for-region-end') }} {{ endRegionName }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Third digit (non-passenger only) -->
|
||||||
|
<div v-if="categoryRules[0] != null">
|
||||||
|
<b>
|
||||||
|
{{ $t('numgen.rules.third-digit') }}
|
||||||
|
<span class="text--accent">{{ categoryRules[0] }}</span>
|
||||||
|
</b>
|
||||||
|
{{ $t('numgen.rules.for-category') }} {{ chosenCategory }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Last digits -->
|
||||||
|
<div>
|
||||||
|
<b>
|
||||||
|
{{
|
||||||
|
$t(`numgen.rules.${categoryRules[1]?.length == 3 ? 'three' : 'two'}-last-digits`)
|
||||||
|
}}</b
|
||||||
|
>
|
||||||
|
{{ $t('numgen.rules.from-range') }}
|
||||||
|
<b class="text--accent">{{ categoryRules[1] }}-{{ categoryRules[2] }}</b>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
<div class="tab_links">
|
||||||
|
<a :href="$t('numgen.td2-wiki-link')" target="_blank">
|
||||||
|
{{ $t('numgen.td2-wiki') }}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
|
||||||
<div class="tab_actions">
|
<div class="tab_actions">
|
||||||
<button class="btn" @click="randomizeTrainNumber">PRZELOSUJ</button>
|
<button class="btn" @click="randomizeTrainNumber(true)">
|
||||||
|
{{ $t('numgen.action-random-region') }}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button class="btn" @click="randomizeCategory">
|
||||||
|
{{ $t('numgen.action-random-category') }}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button class="btn" @click="randomizeTrainNumber(false)">
|
||||||
|
{{ $t('numgen.action-random-number') }}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -38,96 +134,174 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Ref, ref } from 'vue';
|
import { Ref, ref } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
import genData from '../../constants/numberGeneratorData.json';
|
import genData from '../../constants/numberGeneratorData.json';
|
||||||
|
import { computed } from 'vue';
|
||||||
|
|
||||||
|
const i18n = useI18n();
|
||||||
type RegionName = keyof typeof genData.regionNumbers;
|
type RegionName = keyof typeof genData.regionNumbers;
|
||||||
|
type Category = keyof typeof genData.categoriesRules;
|
||||||
|
|
||||||
const beginRegionName = ref(null) as Ref<RegionName | null>;
|
const beginRegionName = ref(null) as Ref<RegionName | null>;
|
||||||
const endRegionName = ref(null) as Ref<RegionName | null>;
|
const endRegionName = ref(null) as Ref<RegionName | null>;
|
||||||
const categoryRules = ref(null) as Ref<string | null>;
|
const chosenCategory = ref(null) as Ref<Category | null>;
|
||||||
|
|
||||||
const trainNumber = ref(null) as Ref<string | null>;
|
const trainNumber = ref(null) as Ref<string | null>;
|
||||||
|
|
||||||
const randomizeTrainNumber = () => {
|
const copyNumber = () => {
|
||||||
if (beginRegionName.value == null || endRegionName.value == null || categoryRules.value == null) return '';
|
if (trainNumber.value) {
|
||||||
|
navigator.clipboard.writeText(trainNumber.value);
|
||||||
|
alert(i18n.t('numgen.alert'));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const categoryRules = computed(() => {
|
||||||
|
if (!chosenCategory.value) return null;
|
||||||
|
|
||||||
|
return genData.categoriesRules[chosenCategory.value];
|
||||||
|
});
|
||||||
|
|
||||||
|
const randomizeCategory = () => {
|
||||||
|
const categoryKeys = Object.keys(genData.categoriesRules) as Category[];
|
||||||
|
chosenCategory.value = categoryKeys[~~(Math.random() * categoryKeys.length)];
|
||||||
|
|
||||||
|
randomizeTrainNumber(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const randomizeTrainNumber = (randomizeRegions = false) => {
|
||||||
|
// if (categoryRules.value == null) return;
|
||||||
|
|
||||||
|
const regionKeys = Object.keys(genData.regionNumbers);
|
||||||
|
|
||||||
|
if (beginRegionName.value == null || randomizeRegions)
|
||||||
|
beginRegionName.value = regionKeys[(regionKeys.length * Math.random()) << 0] as RegionName;
|
||||||
|
|
||||||
|
if (endRegionName.value == null || randomizeRegions)
|
||||||
|
endRegionName.value = regionKeys[(regionKeys.length * Math.random()) << 0] as RegionName;
|
||||||
|
|
||||||
let number = '';
|
let number = '';
|
||||||
|
|
||||||
|
// Two first numbers (begin & end regions)
|
||||||
if (beginRegionName.value == endRegionName.value) {
|
if (beginRegionName.value == endRegionName.value) {
|
||||||
const sameRegionsNumbers = genData.sameRegions[beginRegionName.value];
|
const sameRegionsNumbers = genData.sameRegions[beginRegionName.value!];
|
||||||
const randRegionNumber = sameRegionsNumbers[Math.floor(Math.random() * sameRegionsNumbers.length)];
|
const randRegionNumber =
|
||||||
|
sameRegionsNumbers[Math.floor(Math.random() * sameRegionsNumbers.length)];
|
||||||
number += randRegionNumber.toString();
|
number += randRegionNumber.toString();
|
||||||
} else {
|
} else {
|
||||||
const beginRegionNumber = genData.regionNumbers[beginRegionName.value];
|
const beginRegionNumber = genData.regionNumbers[beginRegionName.value!];
|
||||||
const endRegionNumber = genData.regionNumbers[endRegionName.value];
|
const endRegionNumber = genData.regionNumbers[endRegionName.value!];
|
||||||
|
|
||||||
number += `${beginRegionNumber}${endRegionNumber}`;
|
number += `${beginRegionNumber}${endRegionNumber}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const rulesArray = categoryRules.value.split(';').map((r) => ({
|
// Do not roll the rest of number again if only randomize regions and category rules are already selected
|
||||||
index: r.split(':')[0],
|
if (randomizeRegions && chosenCategory.value != null) {
|
||||||
rule: r.split(':')[1],
|
trainNumber.value = number + trainNumber.value?.substring(2);
|
||||||
nums: Number(r.split(':')[2] || '1'),
|
return;
|
||||||
}));
|
}
|
||||||
|
|
||||||
rulesArray.forEach((r) => {
|
// Choose default category if it's not chosen
|
||||||
const range = r.rule.split('-');
|
if (chosenCategory.value == null) chosenCategory.value = 'EI';
|
||||||
|
|
||||||
if (range.length == 1) number += r.rule;
|
// Get category rules
|
||||||
else {
|
const [thirdNumber, minRange, maxRange] = categoryRules.value!;
|
||||||
const [minRange, maxRange] = range;
|
|
||||||
const randRange = Math.floor(Math.random() * (Number(maxRange) - Number(minRange)) + Number(minRange)).toString();
|
|
||||||
|
|
||||||
number += new Array(Math.abs(randRange.length - r.nums)).fill('0').join('') + randRange;
|
// Third number
|
||||||
}
|
number += thirdNumber ?? '';
|
||||||
});
|
|
||||||
|
// Remaining numbers
|
||||||
|
const rangeNums = minRange!.length;
|
||||||
|
const randRange = Math.floor(
|
||||||
|
Math.random() * (Number(maxRange) - Number(minRange)) + Number(minRange)
|
||||||
|
).toString();
|
||||||
|
const leadingZeros = new Array(Math.abs(randRange.length - rangeNums)).fill('0').join('');
|
||||||
|
|
||||||
|
number += `${leadingZeros}${randRange}`;
|
||||||
|
|
||||||
trainNumber.value = number;
|
trainNumber.value = number;
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import '../../styles/tab.scss';
|
@use '../../styles/tab';
|
||||||
@import '../../styles/global.scss';
|
|
||||||
|
.category-select {
|
||||||
|
select {
|
||||||
|
width: auto;
|
||||||
|
min-width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inputs {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(15em, 1fr));
|
||||||
|
|
||||||
.options {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 0.5em;
|
gap: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
select {
|
.inputs > label {
|
||||||
width: calc(50% - 0.5em);
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.25em;
|
||||||
|
|
||||||
|
span {
|
||||||
|
color: #ccc;
|
||||||
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.generated-number {
|
.generated-number {
|
||||||
font-size: 1.3em;
|
font-size: 1.3em;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
margin: 0.5em 0;
|
margin: 0.5em 0;
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
background-color: $secondaryColor;
|
background-color: global.$secondaryColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-rules {
|
||||||
|
margin-bottom: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab_actions {
|
.tab_actions {
|
||||||
margin-top: 0.5em;
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
margin: 0.5em 0;
|
||||||
button {
|
|
||||||
grid-column: 3;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: $breakpointMd) {
|
.tab_links {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin: 0.25em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: global.$breakpointMd) {
|
||||||
.number-generator {
|
.number-generator {
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: $breakpointSm) {
|
@media screen and (max-width: global.$breakpointSm) {
|
||||||
.options select {
|
.regions-select {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.regions-select select {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.category-select select {
|
||||||
|
min-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-rules {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,62 +1,64 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="stock-generator tab">
|
<div class="stock-generator tab">
|
||||||
<div class="tab_header">
|
|
||||||
<h2>GENERATOR SKŁADU TOWAROWEGO</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="tab_content">
|
<div class="tab_content">
|
||||||
<div>
|
<div>
|
||||||
<h2>WŁAŚCIWOŚCI SKŁADU</h2>
|
<h2>{{ $t('stockgen.properties-title') }}</h2>
|
||||||
|
|
||||||
<b class="text--accent">
|
<b class="text--accent">
|
||||||
⇐ Dodaj lokomotywę na pierwsze miejsce listy, aby uwzględnić ją przy losowaniu składu!
|
{{ $t('stockgen.properties-desc') }}
|
||||||
</b>
|
</b>
|
||||||
|
|
||||||
<div class="tab_attributes">
|
<div class="inputs">
|
||||||
<label>
|
<label>
|
||||||
Maksymalna masa (t)
|
<span>{{ $t('stockgen.input-mass') }}</span>
|
||||||
<input type="number" v-model="maxMass" step="100" max="4000" min="0" />
|
<input type="number" v-model="maxTons" step="100" max="4000" min="0" />
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label>
|
<label>
|
||||||
Maks. długość (m)
|
<span>{{ $t('stockgen.input-length') }}</span>
|
||||||
<input type="number" v-model="maxLength" step="25" max="650" min="0" />
|
<input type="number" v-model="maxLength" step="25" max="650" min="0" />
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label>
|
<label>
|
||||||
Maks. liczba wagonów
|
<span>{{ $t('stockgen.input-carcount') }}</span>
|
||||||
<input type="number" v-model="maxCarCount" step="1" max="60" min="1" />
|
<input type="number" v-model="maxCarCount" step="1" max="60" min="1" />
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- <hr style="margin: 1em 0" /> -->
|
||||||
|
|
||||||
|
<!-- <div class="generator_options">
|
||||||
|
<Checkbox v-model="isCarGroupingEnabled">Grupuj wylosowane wagony (ustawia podobne wagony obok siebie w składzie)</Checkbox>
|
||||||
|
</div> -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h2>ŁADUNEK</h2>
|
<h2>{{ $t('stockgen.cargo-title') }}</h2>
|
||||||
<b>Wybierz ładunki, którymi chcesz wypełnić dostępne wagony:</b>
|
<b>{{ $t('stockgen.cargo-desc') }}</b>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="generator_cargo">
|
<div class="generator_cargo">
|
||||||
<button
|
<button
|
||||||
|
v-for="cargo in computedCargoData"
|
||||||
|
:key="cargo.name"
|
||||||
class="btn"
|
class="btn"
|
||||||
:data-chosen="chosenCargoTypes.includes(k.toString())"
|
:data-chosen="chosenCargoTypes.includes(cargo.name)"
|
||||||
v-for="(v, k) in store.stockData?.generator.cargo"
|
@click="toggleCargoChosen(cargo.name, cargo.cargoList)"
|
||||||
@click="toggleCargoChosen(k.toString(), v)"
|
|
||||||
>
|
>
|
||||||
{{ k }}
|
{{ $t(`cargo.${cargo.name}`) }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h2>WAGONY Z WYBRANYMI ŁADUNKAMI</h2>
|
<h2>{{ $t('stockgen.chosen-title') }}</h2>
|
||||||
|
|
||||||
<div class="generator_warning">
|
<div class="generator_warning">
|
||||||
<span v-if="computedChosenCarTypes.size == 0">
|
<span v-if="computedChosenCarTypes.size == 0">
|
||||||
Wybierz co najmniej jeden ładunek, aby zobaczyć wagony, które go posiadają!
|
{{ $t('stockgen.chosen-empty-warning') }}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span v-else>
|
<span v-else>
|
||||||
Wagony posiadające wybrane ładunki. Najedź na nazwę, aby zobaczyć podgląd wagonu. Kliknij, aby wyłączyć z
|
{{ $t('stockgen.chosen-warning') }}
|
||||||
losowania (tylko podświetlone nazwy będą uwzględnione).
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -79,15 +81,28 @@
|
|||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<div class="tab_actions">
|
<div class="tab_actions">
|
||||||
<button class="btn" :data-disabled="computedChosenCarTypes.size == 0" @click="generateStock()">
|
<button
|
||||||
WYGENERUJ
|
class="btn"
|
||||||
</button>
|
:data-disabled="computedChosenCarTypes.size == 0"
|
||||||
<button class="btn" :data-disabled="computedChosenCarTypes.size == 0" @click="generateStock(true)">
|
@click="generateStock()"
|
||||||
WYGENERUJ PRÓŻNE WAGONY
|
>
|
||||||
|
{{ $t('stockgen.action-generate') }}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button class="btn" :data-disabled="computedChosenCarTypes.size == 0" @click="resetChosenCargo">
|
<button
|
||||||
ZRESETUJ ŁADUNKI
|
class="btn"
|
||||||
|
:data-disabled="computedChosenCarTypes.size == 0"
|
||||||
|
@click="generateStock(true)"
|
||||||
|
>
|
||||||
|
{{ $t('stockgen.action-generate-empty') }}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="btn"
|
||||||
|
:data-disabled="computedChosenCarTypes.size == 0"
|
||||||
|
@click="resetChosenCargo"
|
||||||
|
>
|
||||||
|
{{ $t('stockgen.action-reset') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -99,13 +114,14 @@ import { defineComponent } from 'vue';
|
|||||||
import { useStore } from '../../store';
|
import { useStore } from '../../store';
|
||||||
|
|
||||||
import stockMixin from '../../mixins/stockMixin';
|
import stockMixin from '../../mixins/stockMixin';
|
||||||
import { ICargo, ICarWagon } from '../../types';
|
import { ICargo, ICarWagon, IStock } from '../../types/common.types';
|
||||||
import warningsMixin from '../../mixins/warningsMixin';
|
import { isTractionUnit } from '../../utils/vehicleUtils';
|
||||||
|
|
||||||
|
import generatorDataJSON from '../../data/generatorData.json';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'stock-generator',
|
name: 'stock-generator',
|
||||||
|
mixins: [stockMixin],
|
||||||
mixins: [stockMixin, warningsMixin],
|
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -116,17 +132,30 @@ export default defineComponent({
|
|||||||
|
|
||||||
previewTimeout: -1,
|
previewTimeout: -1,
|
||||||
|
|
||||||
maxMass: 3000,
|
maxTons: 3000,
|
||||||
maxLength: 650,
|
maxLength: 650,
|
||||||
maxCarCount: 50,
|
maxCarCount: 50,
|
||||||
|
|
||||||
|
isCarGroupingEnabled: false,
|
||||||
|
|
||||||
store: useStore(),
|
store: useStore(),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
computedChosenCarTypes() {
|
computedChosenCarTypes() {
|
||||||
return new Set<string>(this.chosenCarTypes.sort((c1, c2) => (c1 > c2 ? 1 : -1)));
|
return new Set<string>(this.chosenCarTypes.slice().sort((c1, c2) => (c1 > c2 ? 1 : -1)));
|
||||||
|
},
|
||||||
|
|
||||||
|
computedCargoData() {
|
||||||
|
const cargoGeneratorData = generatorDataJSON.cargo;
|
||||||
|
|
||||||
|
return Object.keys(cargoGeneratorData)
|
||||||
|
.sort((v1, v2) => this.$t(`cargo.${v1}`).localeCompare(this.$t(`cargo.${v2}`)))
|
||||||
|
.map((v) => ({
|
||||||
|
name: v,
|
||||||
|
cargoList: cargoGeneratorData[v as keyof typeof generatorDataJSON.cargo],
|
||||||
|
}));
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -149,53 +178,101 @@ export default defineComponent({
|
|||||||
this.excludedCarTypes.length = 0;
|
this.excludedCarTypes.length = 0;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// WIP
|
||||||
|
groupStock(stockList: IStock[]) {
|
||||||
|
if (!this.isCarGroupingEnabled) return false;
|
||||||
|
|
||||||
|
stockList.sort((s1, s2) => {
|
||||||
|
return (s1.vehicleRef.constructionType + s1.cargo?.id).localeCompare(
|
||||||
|
s2.vehicleRef.constructionType + s2.cargo?.id
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
generateStock(empty = false) {
|
generateStock(empty = false) {
|
||||||
const generatedChosenStockList = this.chosenCargoTypes.reduce((acc, type) => {
|
const generatedChosenStockList = this.chosenCargoTypes.reduce(
|
||||||
this.store.stockData?.generator.cargo[type]
|
(acc, type) => {
|
||||||
.filter((c) => !this.excludedCarTypes.includes(c.split(':')[0]))
|
generatorDataJSON.cargo[type as keyof typeof generatorDataJSON.cargo]
|
||||||
.forEach((c) => {
|
.filter((c) => !this.excludedCarTypes.includes(c.split(':')[0]))
|
||||||
const [type, cargoType] = c.split(':');
|
.forEach((c) => {
|
||||||
|
const [type, cargoType] = c.split(':');
|
||||||
|
|
||||||
const carWagonObjs = this.store.carDataList.filter((cw) => cw.type.startsWith(type));
|
const carWagonObjs = this.store.carDataList.filter((cw) => cw.type.startsWith(type));
|
||||||
const cargoObjs = [] as (ICargo | undefined)[];
|
const cargoObjs = [] as (ICargo | undefined)[];
|
||||||
|
|
||||||
if (!cargoType || empty) cargoObjs.push(undefined);
|
if (!cargoType || empty) cargoObjs.push(undefined);
|
||||||
else if (cargoType == 'all') cargoObjs.push(...carWagonObjs[0]?.cargoList);
|
else if (cargoType == 'all') cargoObjs.push(...carWagonObjs[0]!.cargoTypes);
|
||||||
else cargoObjs.push(carWagonObjs[0]?.cargoList.find((cargo) => cargo.id == cargoType));
|
else
|
||||||
|
cargoObjs.push(carWagonObjs[0]?.cargoTypes.find((cargo) => cargo.id == cargoType));
|
||||||
|
|
||||||
carWagonObjs.forEach((cw) => {
|
carWagonObjs.forEach((cw) => {
|
||||||
cargoObjs.forEach((cargoObj) => {
|
cargoObjs.forEach((cargoObj) => {
|
||||||
const chosenStock = acc.find((a) => a.constructionType.includes(cw.constructionType));
|
const chosenStock = acc.find((a) =>
|
||||||
|
a.constructionType.includes(cw.constructionType)
|
||||||
|
);
|
||||||
|
|
||||||
if (!chosenStock)
|
if (!chosenStock)
|
||||||
acc.push({
|
acc.push({
|
||||||
constructionType: cw.constructionType,
|
constructionType: cw.constructionType,
|
||||||
carPool: [{ carWagon: cw, cargo: cargoObj }],
|
carPool: [{ carWagon: cw, cargo: cargoObj }],
|
||||||
});
|
});
|
||||||
else chosenStock.carPool.push({ carWagon: cw, cargo: cargoObj });
|
else chosenStock.carPool.push({ carWagon: cw, cargo: cargoObj });
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
return acc;
|
return acc;
|
||||||
}, [] as { constructionType: string; carPool: { carWagon: ICarWagon; cargo?: ICargo }[] }[]);
|
},
|
||||||
|
[] as { constructionType: string; carPool: { carWagon: ICarWagon; cargo?: ICargo }[] }[]
|
||||||
|
);
|
||||||
|
|
||||||
const headingLoco = this.store.stockList[0]?.isLoco ? this.store.stockList[0] : undefined;
|
let bestGeneration: { stockList: IStock[]; value: number } = { stockList: [], value: 0 };
|
||||||
|
|
||||||
this.store.stockList.length = headingLoco ? 1 : 0;
|
for (let i = 0; i < 10; i++) {
|
||||||
const maxMass = this.store.acceptableMass || this.maxMass;
|
this.store.stockList.splice(
|
||||||
|
this.store.stockList.length > 0 && isTractionUnit(this.store.stockList[0].vehicleRef)
|
||||||
|
? 1
|
||||||
|
: 0
|
||||||
|
);
|
||||||
|
|
||||||
new Array(this.maxCarCount).fill(0).forEach(() => {
|
let carCount = 0;
|
||||||
const randomStockType = generatedChosenStockList[~~(Math.random() * generatedChosenStockList.length)];
|
const maxWeight =
|
||||||
const { carWagon, cargo } = randomStockType.carPool[~~(Math.random() * randomStockType.carPool.length)];
|
this.store.acceptableWeight > 0
|
||||||
|
? Math.min(this.store.acceptableWeight, this.maxTons * 1000)
|
||||||
|
: this.maxTons * 1000;
|
||||||
|
|
||||||
if (this.store.totalMass + (cargo?.totalMass || carWagon.mass) > maxMass) return;
|
// eslint-disable-next-line no-constant-condition
|
||||||
if (this.store.totalLength + carWagon.length > this.maxLength) return;
|
while (true) {
|
||||||
|
const randomStockType =
|
||||||
|
generatedChosenStockList[~~(Math.random() * generatedChosenStockList.length)];
|
||||||
|
const { carWagon, cargo } =
|
||||||
|
randomStockType.carPool[~~(Math.random() * randomStockType.carPool.length)];
|
||||||
|
|
||||||
this.addCarWagon(carWagon, cargo);
|
if (
|
||||||
});
|
this.store.totalWeight + (carWagon.weight + (cargo?.weight ?? 0)) > maxWeight ||
|
||||||
|
this.store.totalLength + carWagon.length > this.maxLength ||
|
||||||
|
carCount >= this.maxCarCount
|
||||||
|
) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
this.store.stockSectionMode = 'stock-list';
|
this.addCarWagon(carWagon, cargo);
|
||||||
|
carCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentGenerationValue = this.store.totalLength + this.store.totalWeight + carCount;
|
||||||
|
|
||||||
|
if (bestGeneration.value < currentGenerationValue) {
|
||||||
|
bestGeneration.stockList = this.store.stockList;
|
||||||
|
bestGeneration.value = currentGenerationValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const bestStockList = bestGeneration.stockList;
|
||||||
|
|
||||||
|
this.store.stockList = bestGeneration.stockList;
|
||||||
|
this.groupStock(bestStockList);
|
||||||
|
this.$router.push('/');
|
||||||
},
|
},
|
||||||
|
|
||||||
previewCar(type: string) {
|
previewCar(type: string) {
|
||||||
@@ -206,7 +283,7 @@ export default defineComponent({
|
|||||||
this.store.chosenLoco = null;
|
this.store.chosenLoco = null;
|
||||||
this.store.chosenCargo = null;
|
this.store.chosenCargo = null;
|
||||||
|
|
||||||
if (c) this.store.chosenCarUseType = c?.useType;
|
if (c) this.store.chosenCarGroup = c?.group;
|
||||||
},
|
},
|
||||||
|
|
||||||
toggleCargoChosen(cargoType: string, vehicles: string[]) {
|
toggleCargoChosen(cargoType: string, vehicles: string[]) {
|
||||||
@@ -237,14 +314,18 @@ export default defineComponent({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import '../../styles/global.scss';
|
@use '@/styles/tab';
|
||||||
@import '../../styles/tab.scss';
|
|
||||||
|
h2 {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
.generator_cargo,
|
.generator_cargo,
|
||||||
.generator_vehicles {
|
.generator_vehicles {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 0.5em;
|
gap: 0.5em;
|
||||||
grid-template-columns: repeat(4, 1fr);
|
grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
|
||||||
|
|
||||||
button {
|
button {
|
||||||
position: relative;
|
position: relative;
|
||||||
@@ -254,14 +335,7 @@ export default defineComponent({
|
|||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
|
||||||
background-color: $secondaryColor;
|
background-color: global.$secondaryColor;
|
||||||
|
|
||||||
&[data-chosen='true'] {
|
|
||||||
background-color: $accentColor;
|
|
||||||
color: black;
|
|
||||||
|
|
||||||
box-shadow: 0 0 5px 1px $accentColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
&[data-excluded='true'] {
|
&[data-excluded='true'] {
|
||||||
background-color: gray;
|
background-color: gray;
|
||||||
@@ -275,7 +349,7 @@ export default defineComponent({
|
|||||||
padding: 5px;
|
padding: 5px;
|
||||||
|
|
||||||
transform: translate(-8px, -50%);
|
transform: translate(-8px, -50%);
|
||||||
background-color: $bgColor;
|
background-color: global.$bgColor;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -287,19 +361,39 @@ export default defineComponent({
|
|||||||
gap: 1em;
|
gap: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.inputs {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(12em, 1fr));
|
||||||
|
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inputs > label {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.25em;
|
||||||
|
|
||||||
|
span {
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type='text'] {
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.generator_options {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
.generator_warning {
|
.generator_warning {
|
||||||
background-color: $accentColor;
|
background-color: global.$accentColor;
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
text-align: justify;
|
text-align: justify;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: black;
|
color: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media only screen and (max-width: 470px) {
|
|
||||||
.generator_cargo,
|
|
||||||
.generator_vehicles {
|
|
||||||
grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,585 +1,52 @@
|
|||||||
<template>
|
<template>
|
||||||
<section class="stock-list">
|
<section class="stock-list-tab">
|
||||||
<div class="stock_controls" :data-disabled="store.chosenStockListIndex == -1">
|
<div class="tab_content">
|
||||||
<b class="no">
|
<!-- Stock Actions -->
|
||||||
POJAZD NR <span class="text--accent">{{ store.chosenStockListIndex + 1 }}</span>
|
<StockActions />
|
||||||
</b>
|
|
||||||
|
|
||||||
<button
|
<!-- Stock Specs -->
|
||||||
class="btn"
|
<StockSpecs />
|
||||||
:tabindex="store.chosenStockListIndex == -1 ? -1 : 0"
|
|
||||||
@click="moveUpStock(store.chosenStockListIndex)"
|
|
||||||
>
|
|
||||||
<img :src="getIconURL('higher')" alt="move up vehicle" />
|
|
||||||
PRZENIEŚ WYŻEJ
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button
|
<!-- Stock Spawn Settings -->
|
||||||
class="btn"
|
<StockSpawnSettings />
|
||||||
:tabindex="store.chosenStockListIndex == -1 ? -1 : 0"
|
|
||||||
@click="moveDownStock(store.chosenStockListIndex)"
|
|
||||||
>
|
|
||||||
<img :src="getIconURL('lower')" alt="move down vehicle" />
|
|
||||||
PRZENIEŚ NIŻEJ
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button
|
<!-- Stock Warnings -->
|
||||||
class="btn"
|
<StockWarnings />
|
||||||
:tabindex="store.chosenStockListIndex == -1 ? -1 : 0"
|
|
||||||
@click="removeStock(store.chosenStockListIndex)"
|
<!-- Stock List -->
|
||||||
>
|
<StockList />
|
||||||
<img :src="getIconURL('remove')" alt="remove vehicle" />
|
|
||||||
USUŃ
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="stock_actions">
|
|
||||||
<label class="file-label">
|
|
||||||
<div class="btn">WCZYTAJ</div>
|
|
||||||
<input type="file" @change="uploadStock" ref="conFile" accept=".con,.txt" />
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<button class="btn" :data-disabled="stockIsEmpty" :disabled="stockIsEmpty" @click="downloadStock">POBIERZ</button>
|
|
||||||
|
|
||||||
<button class="btn" :data-disabled="stockIsEmpty" :disabled="stockIsEmpty" @click="copyToClipboard">
|
|
||||||
SKOPIUJ
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button class="btn" :data-disabled="stockIsEmpty" :disabled="stockIsEmpty" @click="resetStock">ZRESETUJ</button>
|
|
||||||
|
|
||||||
<button class="btn" :data-disabled="stockIsEmpty" :disabled="stockIsEmpty" @click="shuffleCars">PRZETASUJ</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="stock_specs">
|
|
||||||
<b class="real-stock-info" v-if="store.chosenRealStock">
|
|
||||||
<span class="text--accent">
|
|
||||||
<img :src="getIconURL(store.chosenRealStock.type)" :alt="store.chosenRealStock.type" />
|
|
||||||
{{ store.chosenRealStock.number }} {{ store.chosenRealStock.name }}
|
|
||||||
</span>
|
|
||||||
|
|
|
||||||
</b>
|
|
||||||
|
|
||||||
<span>
|
|
||||||
Masa: <span class="text--accent">{{ store.totalMass }}t</span> (dopuszczalna:
|
|
||||||
<span class="text--accent">{{ store.acceptableMass ? store.acceptableMass + 't' : '-' }}</span
|
|
||||||
>) - Długość:
|
|
||||||
<span class="text--accent">{{ store.totalLength }}m</span>
|
|
||||||
- vMax: <span class="text--accent">{{ store.maxStockSpeed }} km/h</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="stock_warnings">
|
|
||||||
<div class="warning" v-if="locoNotSuitable">
|
|
||||||
Lokomotywy EP07 i EP08 są przeznaczone jedynie do ruchu pasażerskiego!
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="warning" v-if="trainTooLong && store.isTrainPassenger">
|
|
||||||
Maksymalna długość składów pasażerskich nie może przekraczać 350m!
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="warning" v-if="trainTooLong && !store.isTrainPassenger">
|
|
||||||
Maksymalna długość składów innych niż pasażerskie nie może przekraczać 650m!
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="warning" v-if="trainTooHeavy">
|
|
||||||
Ten skład jest za ciężki! Sprawdź
|
|
||||||
<a
|
|
||||||
target="_blank"
|
|
||||||
href="https://docs.google.com/spreadsheets/d/1bFXUsHsAu4youmNz-46Q1HslZaaoklvfoBDS553TnNk/edit"
|
|
||||||
>
|
|
||||||
dopuszczalne masy składów
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="warning" v-if="tooManyLocomotives">Ten skład posiada za dużo pojazdów trakcyjnych!</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<StockThumbnails :onListItemClick="onListItemClick" :onStockImageError="stockImageError" />
|
|
||||||
|
|
||||||
<!-- Stock list -->
|
|
||||||
<ul ref="stock_list">
|
|
||||||
<li v-if="stockIsEmpty" class="list-empty">
|
|
||||||
<div class="stock-info">Lista pojazdów jest pusta!</div>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<TransitionGroup 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"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="stock-info"
|
|
||||||
@dragstart="onDragStart(i)"
|
|
||||||
@drop="onDrop($event, i)"
|
|
||||||
@dragover="allowDrop"
|
|
||||||
draggable="true"
|
|
||||||
>
|
|
||||||
<span class="stock-info__no" :data-selected="i == store.chosenStockListIndex">
|
|
||||||
<span v-if="i == store.chosenStockListIndex">• </span>
|
|
||||||
{{ i + 1 }}.
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span class="stock-info__type">
|
|
||||||
{{ 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.cargo ? stock.cargo.totalMass : stock.mass }}t </span>
|
|
||||||
<span class="stock-info__speed"> {{ stock.maxSpeed }}km/h </span>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
</TransitionGroup>
|
|
||||||
</ul>
|
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import TrainImage from '../sections/TrainImageSection.vue';
|
|
||||||
|
|
||||||
import { useStore } from '../../store';
|
import StockActions from './stock-list/StockActions.vue';
|
||||||
import warningsMixin from '../../mixins/warningsMixin';
|
import StockSpecs from './stock-list/StockSpecs.vue';
|
||||||
import imageMixin from '../../mixins/imageMixin';
|
import StockSpawnSettings from './stock-list/StockSpawnSettings.vue';
|
||||||
import stockPreviewMixin from '../../mixins/stockPreviewMixin';
|
import StockWarnings from './stock-list/StockWarnings.vue';
|
||||||
import { IStock } from '../../types';
|
import StockList from './stock-list/StockList.vue';
|
||||||
import StockThumbnails from '../utils/StockThumbnails.vue';
|
|
||||||
import stockMixin from '../../mixins/stockMixin';
|
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'stock-list',
|
name: 'stock-list',
|
||||||
components: { TrainImage, StockThumbnails },
|
|
||||||
|
|
||||||
mixins: [warningsMixin, imageMixin, stockMixin, stockPreviewMixin],
|
components: {
|
||||||
|
StockActions,
|
||||||
setup() {
|
StockSpecs,
|
||||||
const store = useStore();
|
StockSpawnSettings,
|
||||||
|
StockWarnings,
|
||||||
return {
|
StockList,
|
||||||
store,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
data: () => ({
|
|
||||||
imageOffsetY: 0,
|
|
||||||
|
|
||||||
draggedVehicleID: -1,
|
|
||||||
}),
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
stockString() {
|
|
||||||
return this.store.stockList
|
|
||||||
.map((stock) => {
|
|
||||||
let s = stock.isLoco || !stock.cargo ? stock.type : `${stock.type}:${stock.cargo.id}`;
|
|
||||||
|
|
||||||
let final = s;
|
|
||||||
for (let i = 0; i < stock.count - 1; i++) final += `;${s}`;
|
|
||||||
|
|
||||||
return final;
|
|
||||||
})
|
|
||||||
.join(';');
|
|
||||||
},
|
|
||||||
|
|
||||||
stockIsEmpty() {
|
|
||||||
return this.store.stockList.length == 0;
|
|
||||||
},
|
|
||||||
|
|
||||||
chosenStockVehicle() {
|
|
||||||
return this.store.chosenStockListIndex == -1 ? undefined : this.store.stockList[this.store.chosenStockListIndex];
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
stockHasWarnings() {
|
|
||||||
return this.tooManyLocomotives || this.trainTooHeavy || this.trainTooLong || this.locoNotSuitable;
|
|
||||||
},
|
|
||||||
|
|
||||||
stockImageError(e: Event, stock: IStock): void {
|
|
||||||
(e.target as HTMLImageElement).src = `images/${stock.useType}-unknown.png`;
|
|
||||||
},
|
|
||||||
|
|
||||||
copyToClipboard() {
|
|
||||||
// if (this.stockHasWarnings()) {
|
|
||||||
// alert('Jazda tym pociągiem jest niezgodna z regulaminem symulatora! Zmień parametry zestawienia!');
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
navigator.clipboard.writeText(this.stockString);
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
alert('Pociąg został skopiowany do schowka!');
|
|
||||||
}, 20);
|
|
||||||
},
|
|
||||||
|
|
||||||
onListItemClick(stockID: number) {
|
|
||||||
const stock = this.store.stockList[stockID];
|
|
||||||
|
|
||||||
this.store.chosenStockListIndex =
|
|
||||||
this.store.chosenStockListIndex == stockID && this.store.chosenVehicle?.type == stock.type ? -1 : stockID;
|
|
||||||
|
|
||||||
if (this.store.chosenStockListIndex == -1) {
|
|
||||||
this.store.chosenVehicle = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.store.swapVehicles) this.store.swapVehicles = false;
|
|
||||||
|
|
||||||
this.previewStock(stock);
|
|
||||||
},
|
|
||||||
|
|
||||||
getCarSpecFromType(typeStr: string) {
|
|
||||||
const specArray = typeStr.split('_');
|
|
||||||
|
|
||||||
if (specArray.length == 0) return null;
|
|
||||||
|
|
||||||
/* 111a_Grafitti_1 */
|
|
||||||
if (specArray.length == 3) return `${specArray[0]} ${specArray[1]}-${specArray[2]}`;
|
|
||||||
|
|
||||||
/* 111a_PKP_Bnouz_01 */
|
|
||||||
return `${specArray[0]} ${specArray[2]}-${specArray[3]} (${specArray[1]})`;
|
|
||||||
},
|
|
||||||
|
|
||||||
resetStock() {
|
|
||||||
this.store.stockList.length = 0;
|
|
||||||
this.store.chosenStockListIndex = -1;
|
|
||||||
},
|
|
||||||
|
|
||||||
addStock(index: number) {
|
|
||||||
if (index == -1) return;
|
|
||||||
|
|
||||||
this.store.stockList[index].count++;
|
|
||||||
},
|
|
||||||
|
|
||||||
subStock(index: number) {
|
|
||||||
if (index == -1) return;
|
|
||||||
|
|
||||||
if (this.store.stockList[index].count < 2) return;
|
|
||||||
|
|
||||||
this.store.stockList[index].count--;
|
|
||||||
},
|
|
||||||
|
|
||||||
removeStock(index: number) {
|
|
||||||
if (index == -1) return;
|
|
||||||
|
|
||||||
this.store.stockList = this.store.stockList.filter((stock, i) => i != index);
|
|
||||||
|
|
||||||
if (this.store.stockList.length < index + 1) this.store.chosenStockListIndex = -1;
|
|
||||||
},
|
|
||||||
|
|
||||||
moveUpStock(index: number) {
|
|
||||||
if (index < 1) return;
|
|
||||||
|
|
||||||
const tempStock = this.store.stockList[index];
|
|
||||||
|
|
||||||
this.store.stockList[index] = this.store.stockList[index - 1];
|
|
||||||
this.store.stockList[index - 1] = tempStock;
|
|
||||||
|
|
||||||
this.store.chosenStockListIndex = index - 1;
|
|
||||||
},
|
|
||||||
|
|
||||||
moveDownStock(index: number) {
|
|
||||||
if (index == -1) return;
|
|
||||||
if (index > this.store.stockList.length - 2) return;
|
|
||||||
|
|
||||||
const tempStock = this.store.stockList[index];
|
|
||||||
|
|
||||||
this.store.stockList[index] = this.store.stockList[index + 1];
|
|
||||||
this.store.stockList[index + 1] = tempStock;
|
|
||||||
|
|
||||||
this.store.chosenStockListIndex = index + 1;
|
|
||||||
},
|
|
||||||
|
|
||||||
shuffleCars() {
|
|
||||||
const availableIndexes = this.store.stockList.reduce((acc, stock, i) => {
|
|
||||||
if (!stock.isLoco) acc.push(i);
|
|
||||||
|
|
||||||
return acc;
|
|
||||||
}, [] as number[]);
|
|
||||||
|
|
||||||
for (let i = 0; i < this.store.stockList.length; i++) {
|
|
||||||
if (!availableIndexes.includes(i)) continue;
|
|
||||||
|
|
||||||
availableIndexes.splice(i, -1);
|
|
||||||
|
|
||||||
const randAvailableIndex = availableIndexes[Math.floor(Math.random() * availableIndexes.length)];
|
|
||||||
const tempSwap = this.store.stockList[randAvailableIndex];
|
|
||||||
|
|
||||||
this.store.stockList[randAvailableIndex] = this.store.stockList[i];
|
|
||||||
this.store.stockList[i] = tempSwap;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
downloadStock() {
|
|
||||||
if (this.store.stockList.length == 0) return alert('Lista pojazdów jest pusta!');
|
|
||||||
|
|
||||||
// if (this.stockHasWarnings())
|
|
||||||
// return alert('Jazda tym pociągiem jest niezgodna z regulaminem symulatora! Zmień parametry zestawienia!');
|
|
||||||
|
|
||||||
const defaultName = `${this.store.chosenRealStockName || this.store.stockList[0].type} ${
|
|
||||||
this.store.totalMass
|
|
||||||
}t; ${this.store.totalLength}m; vmax ${this.store.maxStockSpeed}`;
|
|
||||||
|
|
||||||
const fileName = prompt(
|
|
||||||
'Nazwij plik, a następnie pobierz do folderu Presets (Dokumenty/TTSK/TrainDriver2):',
|
|
||||||
defaultName
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!fileName) return;
|
|
||||||
|
|
||||||
const blob = new Blob([this.stockString]);
|
|
||||||
const file = fileName + '.con';
|
|
||||||
|
|
||||||
var e = document.createEvent('MouseEvents'),
|
|
||||||
a = document.createElement('a');
|
|
||||||
a.download = file;
|
|
||||||
a.href = window.URL.createObjectURL(blob);
|
|
||||||
a.dataset.downloadurl = ['', a.download, a.href].join(':');
|
|
||||||
e.initEvent('click', true, false);
|
|
||||||
a.dispatchEvent(e);
|
|
||||||
},
|
|
||||||
|
|
||||||
uploadStock() {
|
|
||||||
const inputEl = this.$refs['conFile'] as HTMLInputElement;
|
|
||||||
const files = inputEl.files;
|
|
||||||
|
|
||||||
if (files?.length != 1) return;
|
|
||||||
if (!/\.con$/.test(files[0].name)) return;
|
|
||||||
|
|
||||||
const reader = new FileReader();
|
|
||||||
reader.readAsText(files[0]);
|
|
||||||
|
|
||||||
reader.onload = (res) => {
|
|
||||||
const stockString = res.target?.result;
|
|
||||||
|
|
||||||
if (!stockString || typeof stockString !== 'string') return;
|
|
||||||
|
|
||||||
this.loadStockFromString(stockString);
|
|
||||||
};
|
|
||||||
|
|
||||||
reader.onerror = (err) => console.log(err);
|
|
||||||
|
|
||||||
inputEl.value = '';
|
|
||||||
},
|
|
||||||
|
|
||||||
onDragStart(vehicleIndex: number) {
|
|
||||||
this.draggedVehicleID = vehicleIndex;
|
|
||||||
},
|
|
||||||
|
|
||||||
onDrop(e: DragEvent, vehicleIndex: number) {
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
let targetEl = (this.$refs['itemRefs'] as Element[])[vehicleIndex];
|
|
||||||
|
|
||||||
if (!targetEl) return;
|
|
||||||
|
|
||||||
const tempVehicle = this.store.stockList[vehicleIndex];
|
|
||||||
|
|
||||||
this.store.stockList[vehicleIndex] = this.store.stockList[this.draggedVehicleID];
|
|
||||||
this.store.stockList[this.draggedVehicleID] = tempVehicle;
|
|
||||||
|
|
||||||
this.store.chosenStockListIndex = vehicleIndex;
|
|
||||||
},
|
|
||||||
|
|
||||||
allowDrop(e: DragEvent) {
|
|
||||||
e.preventDefault();
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import '../../styles/global';
|
@use '@/styles/tab';
|
||||||
|
|
||||||
.warning {
|
.tab_content {
|
||||||
padding: 0.25em;
|
|
||||||
background: $accentColor;
|
|
||||||
color: black;
|
|
||||||
|
|
||||||
font-weight: bold;
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: black;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.stock_controls {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
flex-direction: column;
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
gap: 0.5em;
|
gap: 0.5em;
|
||||||
flex-wrap: wrap;
|
|
||||||
|
|
||||||
padding: 0.5em;
|
|
||||||
margin-bottom: 1em;
|
|
||||||
|
|
||||||
background-color: #353a57;
|
|
||||||
|
|
||||||
&[data-disabled='true'] {
|
|
||||||
opacity: 0.8;
|
|
||||||
|
|
||||||
user-select: none;
|
|
||||||
-moz-user-select: none;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
input#stock-count {
|
|
||||||
width: 3em;
|
|
||||||
|
|
||||||
margin: 0;
|
|
||||||
padding: 0.25em;
|
|
||||||
outline: none;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
img {
|
|
||||||
margin-right: 0.25em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.stock_actions {
|
|
||||||
display: grid;
|
|
||||||
gap: 0.5em;
|
|
||||||
margin-bottom: 1em;
|
|
||||||
|
|
||||||
grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
|
|
||||||
|
|
||||||
label.file-label {
|
|
||||||
text-align: center;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
input {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.real-stock-info {
|
|
||||||
img {
|
|
||||||
height: 1.3ch;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
overflow: auto;
|
|
||||||
|
|
||||||
height: 500px;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul > li {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
min-width: 500px;
|
|
||||||
|
|
||||||
margin: 0.25em 0;
|
|
||||||
|
|
||||||
outline: none;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&:focus-visible {
|
|
||||||
outline: 1px solid white;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.list-empty {
|
|
||||||
background-color: $secondaryColor;
|
|
||||||
border-radius: 0.5em;
|
|
||||||
padding: 0.75em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
li > .stock-info {
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
color: white;
|
|
||||||
font-weight: 700;
|
|
||||||
|
|
||||||
transition: color 100ms;
|
|
||||||
|
|
||||||
& > span {
|
|
||||||
padding: 0.5em;
|
|
||||||
margin-right: 0.25em;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.stock_warnings {
|
|
||||||
margin: 0.5em 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stock-info {
|
|
||||||
&__no,
|
|
||||||
&__type {
|
|
||||||
background-color: $secondaryColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__count {
|
|
||||||
background-color: #e04e3e;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__no {
|
|
||||||
min-width: 3.5em;
|
|
||||||
text-align: right;
|
|
||||||
|
|
||||||
&[data-selected='true'] {
|
|
||||||
color: $accentColor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__cargo {
|
|
||||||
background-color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__length,
|
|
||||||
&__mass,
|
|
||||||
&__speed {
|
|
||||||
background-color: #555;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.stock-list-anim {
|
|
||||||
&-move, /* apply transition to moving elements */
|
|
||||||
&-enter-active,
|
|
||||||
&-leave-active {
|
|
||||||
transition: all 250ms ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-enter-from {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateY(-25px);
|
|
||||||
}
|
|
||||||
|
|
||||||
&-leave-to {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-leave-active {
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: $breakpointMd) {
|
|
||||||
ul {
|
|
||||||
min-height: auto;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,228 @@
|
|||||||
|
<template>
|
||||||
|
<section class="tab storage-tab">
|
||||||
|
<div class="tab_header">
|
||||||
|
<h2>{{ $t('storage.title') }}</h2>
|
||||||
|
<h3>{{ $t('storage.subtitle') }}</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tab_content">
|
||||||
|
<div class="storage-list-wrapper">
|
||||||
|
<transition-group name="storage-list-anim" tag="ul" class="storage-list">
|
||||||
|
<li v-for="storageEntry in storageStockDataList" :key="storageEntry.id">
|
||||||
|
<div class="storage-item-top">
|
||||||
|
<h3>
|
||||||
|
{{ storageEntry.id }}
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<div class="storage-item-top-actions">
|
||||||
|
<button class="btn btn--icon" @click="chooseStorageStock(storageEntry.id)">
|
||||||
|
<LogIn />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button class="btn btn--icon" @click="toggleStorageEntryExpand(storageEntry.id)">
|
||||||
|
<ChevronDown v-if="!expandedEntries.includes(storageEntry.id)" />
|
||||||
|
<ChevronUp v-else />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button class="btn btn--icon" @click="removeStockIndexFromStorage(storageEntry.id)">
|
||||||
|
<Trash2 />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="storage-item-expandable" v-if="expandedEntries.includes(storageEntry.id)">
|
||||||
|
<i>
|
||||||
|
{{ $t('storage.created-at') }}
|
||||||
|
{{ new Date(storageEntry.createdAt).toLocaleString($i18n.locale) }}</i
|
||||||
|
>
|
||||||
|
<i v-if="storageEntry.updatedAt">
|
||||||
|
• {{ $t('storage.updated-at') }}
|
||||||
|
{{ new Date(storageEntry.updatedAt).toLocaleString($i18n.locale) }}</i
|
||||||
|
>
|
||||||
|
|
||||||
|
<div style="margin-top: 0.5em">
|
||||||
|
<i>{{ $t('storage.stock-title') }} </i>
|
||||||
|
{{ shortenStockString(storageEntry.stockString) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li v-if="Object.keys(storageStockDataList).length == 0" class="storage-no-entries">
|
||||||
|
{{ $t('storage.no-entires') }}
|
||||||
|
</li>
|
||||||
|
</transition-group>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from 'vue';
|
||||||
|
|
||||||
|
import { useStore } from '../../store';
|
||||||
|
import stockMixin from '../../mixins/stockMixin';
|
||||||
|
import { ChevronDown, ChevronUp, LogIn, Trash2 } from 'lucide-vue-next';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
components: {
|
||||||
|
ChevronDown,
|
||||||
|
ChevronUp,
|
||||||
|
LogIn,
|
||||||
|
Trash2,
|
||||||
|
},
|
||||||
|
|
||||||
|
mixins: [stockMixin],
|
||||||
|
|
||||||
|
data: () => ({
|
||||||
|
store: useStore(),
|
||||||
|
expandedEntries: [] as string[],
|
||||||
|
}),
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
storageStockDataList() {
|
||||||
|
return Object.values(this.store.storageStockData)
|
||||||
|
.sort((a, b) => (b.updatedAt ?? b.createdAt) - (a.updatedAt ?? a.createdAt))
|
||||||
|
.map((data) => ({
|
||||||
|
...data,
|
||||||
|
isExpanded: false,
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
shortenStockString(stockString: string) {
|
||||||
|
return Array.from(
|
||||||
|
stockString.split(';').reduce(
|
||||||
|
(acc, s) => {
|
||||||
|
const stockName = s.split(/:|,/)[0];
|
||||||
|
acc.set(stockName, (acc.get(stockName) ?? 0) + 1);
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
new Map() as Map<string, number>
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.map(([stockName, count]) => `${count}x ${stockName.replace(/_/g, ' ')}`)
|
||||||
|
.join(', ');
|
||||||
|
},
|
||||||
|
removeStockIndexFromStorage(stockName: string) {
|
||||||
|
let removeConfirm = confirm(this.$t('storage.remove-confirm'));
|
||||||
|
|
||||||
|
if (!removeConfirm) return;
|
||||||
|
|
||||||
|
delete this.store.storageStockData[stockName];
|
||||||
|
this.store.chosenStorageStockName = '';
|
||||||
|
this.store.chosenStorageStockString = '';
|
||||||
|
|
||||||
|
try {
|
||||||
|
localStorage.setItem('savedStockData', JSON.stringify(this.store.storageStockData));
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Wystąpił błąd podczas usuwania składu z localStorage!', error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
chooseStorageStock(stockName: string) {
|
||||||
|
try {
|
||||||
|
this.loadStockFromString(this.store.storageStockData[stockName].stockString);
|
||||||
|
this.store.chosenStorageStockName = stockName;
|
||||||
|
this.store.chosenStorageStockString = this.store.storageStockData[stockName].stockString;
|
||||||
|
|
||||||
|
this.$router.push('/');
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
toggleStorageEntryExpand(stockName: string) {
|
||||||
|
const stockIndex = this.expandedEntries.indexOf(stockName);
|
||||||
|
|
||||||
|
if (stockIndex == -1) this.expandedEntries.push(stockName);
|
||||||
|
else this.expandedEntries.splice(stockIndex, 1);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@use '@/styles/tab';
|
||||||
|
|
||||||
|
.tab_actions {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
.storage-list-wrapper {
|
||||||
|
position: relative;
|
||||||
|
height: 730px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.storage-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5em;
|
||||||
|
margin-top: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.storage-list > li {
|
||||||
|
padding: 0.5em;
|
||||||
|
background-color: global.$secondaryColor;
|
||||||
|
|
||||||
|
&[data-current='true'] {
|
||||||
|
background-color: #3b3b3b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.storage-item-top {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.storage-item-top > h3 {
|
||||||
|
width: 100%;
|
||||||
|
text-align: left;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.storage-item-top-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5em;
|
||||||
|
|
||||||
|
& > button {
|
||||||
|
background-color: #333;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.storage-item-expandable {
|
||||||
|
margin-top: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.storage-no-entries {
|
||||||
|
padding: 1em;
|
||||||
|
font-size: 1.15em;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.storage-list-anim {
|
||||||
|
&-move,
|
||||||
|
&-enter-active,
|
||||||
|
&-leave-active {
|
||||||
|
transition: all 120ms ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-enter-from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(-25px);
|
||||||
|
}
|
||||||
|
|
||||||
|
&-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-leave-active {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,361 @@
|
|||||||
|
<template>
|
||||||
|
<section class="wiki-list tab">
|
||||||
|
<div class="tab_content">
|
||||||
|
<div class="actions">
|
||||||
|
<div class="action action-input">
|
||||||
|
<label for="search-vehicle">
|
||||||
|
{{ $t('wiki.labels.search-vehicle') }}
|
||||||
|
<button class="reset-btn" @click="resetSearchInput">
|
||||||
|
<img src="/images/icon-exit.svg" alt="reset vehicle input icon" />
|
||||||
|
</button>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="search-vehicle"
|
||||||
|
name="search-vehicle"
|
||||||
|
:placeholder="$t('wiki.labels.search-vehicle-placeholder')"
|
||||||
|
v-model="searchedVehicleTypeName"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="action action-select">
|
||||||
|
<label for="filter-type">{{ $t('wiki.labels.vehicles') }}</label>
|
||||||
|
<select name="filter-type" id="filter-type" v-model="filterType">
|
||||||
|
<option v-for="filter in filters" :key="filter" :value="filter">
|
||||||
|
{{ $t(`wiki.filters.${filter}`) }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="action action-select">
|
||||||
|
<label for="sorter-type">{{ $t('wiki.labels.sort-by') }}</label>
|
||||||
|
<select name="sorter-type" id="sorter-type" v-model="sorterType">
|
||||||
|
<option v-for="sorter in sorters" :key="sorter" :value="sorter">
|
||||||
|
{{ $t(`wiki.sort-by.${sorter}`) }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="action action-select">
|
||||||
|
<label for="sorter-direction">{{ $t('wiki.labels.sort-direction') }}</label>
|
||||||
|
|
||||||
|
<select name="sorter-direction" id="sorter-direction" v-model="sorterDirection">
|
||||||
|
<option value="asc">{{ $t('wiki.sort-direction.asc') }}</option>
|
||||||
|
<option value="desc">{{ $t('wiki.sort-direction.desc') }}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ul class="vehicles" ref="vehicles">
|
||||||
|
<li
|
||||||
|
v-for="vehicle in computedVehicles"
|
||||||
|
:key="vehicle.type"
|
||||||
|
:data-preview="vehicle.type === store.chosenVehicle?.type"
|
||||||
|
@click="previewVehicle(vehicle)"
|
||||||
|
@dblclick="addVehicle(vehicle)"
|
||||||
|
@keydown.enter="onVehicleSelect(vehicle)"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
loading="lazy"
|
||||||
|
width="120"
|
||||||
|
:src="getThumbnailURL(vehicle.type, 'small')"
|
||||||
|
@error="onThumbnailImageError"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<span>
|
||||||
|
<span
|
||||||
|
class="vehicle-name"
|
||||||
|
:class="{
|
||||||
|
'sponsor-only':
|
||||||
|
vehicle.sponsorOnlyTimestamp && vehicle.sponsorOnlyTimestamp > Date.now(),
|
||||||
|
'team-only': vehicle.teamOnly,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<b>{{ vehicle.type.replace(/_/g, ' ') }}</b>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<div class="vehicle-group">
|
||||||
|
{{ $t(`wiki.${vehicle.group}`) }} |
|
||||||
|
{{ isTractionUnit(vehicle) ? vehicle.cabinType : vehicle.constructionType }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="vehicle-props">
|
||||||
|
{{ vehicle.length }}m | {{ (vehicle.weight / 1000).toFixed(1) }}t |
|
||||||
|
{{ vehicle.maxSpeed }}km/h
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="no-vehicles-warning" v-if="computedVehicles.length == 0">
|
||||||
|
{{ $t('wiki.no-vehicles') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from 'vue';
|
||||||
|
import { useStore } from '../../store';
|
||||||
|
import stockPreviewMixin from '../../mixins/stockPreviewMixin';
|
||||||
|
import { IVehicle } from '../../types/common.types';
|
||||||
|
import { isTractionUnit } from '../../utils/vehicleUtils';
|
||||||
|
import stockMixin from '../../mixins/stockMixin';
|
||||||
|
import imageMixin from '../../mixins/imageMixin';
|
||||||
|
|
||||||
|
const sorters = ['type', 'group', 'length', 'weight', 'maxSpeed'] as const;
|
||||||
|
|
||||||
|
enum VehicleFilter {
|
||||||
|
AllVehicles = 'vehicles-all',
|
||||||
|
AllTractions = 'vehicles-tractions-all',
|
||||||
|
ElectricTraction = 'vehicles-tractions-electric',
|
||||||
|
DieselTraction = 'vehicles-tractions-diesel',
|
||||||
|
EmuTraction = 'vehicles-tractions-emu',
|
||||||
|
DmuTraction = 'vehicles-tractions-dmu',
|
||||||
|
AllWagons = 'vehicles-wagons-all',
|
||||||
|
PassengerWagons = 'vehicles-wagons-passenger',
|
||||||
|
FreightWagons = 'vehicles-wagons-freight',
|
||||||
|
}
|
||||||
|
|
||||||
|
type SorterType = (typeof sorters)[number];
|
||||||
|
type SorterDirection = 'asc' | 'desc';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
mixins: [stockPreviewMixin, stockMixin, imageMixin],
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
store: useStore(),
|
||||||
|
observer: null as IntersectionObserver | null,
|
||||||
|
|
||||||
|
sorters: sorters,
|
||||||
|
filters: Object.values(VehicleFilter),
|
||||||
|
|
||||||
|
searchedVehicleTypeName: '',
|
||||||
|
|
||||||
|
sorterType: 'type' as SorterType,
|
||||||
|
sorterDirection: 'asc' as SorterDirection,
|
||||||
|
|
||||||
|
filterType: VehicleFilter.AllVehicles,
|
||||||
|
|
||||||
|
lastScrollTop: 0,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
deactivated() {
|
||||||
|
this.lastScrollTop = (this.$refs['vehicles'] as HTMLUListElement)?.scrollTop || 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
activated() {
|
||||||
|
(this.$refs['vehicles'] as HTMLUListElement)?.scrollTo({ top: this.lastScrollTop });
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
computedVehicles() {
|
||||||
|
const vehiclesRef = this.$refs['vehicles'] as HTMLElement;
|
||||||
|
|
||||||
|
vehiclesRef.scrollTo({
|
||||||
|
top: 0,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
isTractionUnit,
|
||||||
|
|
||||||
|
onThumbnailImageError(e: Event) {
|
||||||
|
const el = e.target as HTMLImageElement;
|
||||||
|
|
||||||
|
if (el.src == '/images/no-vehicle-image.png') return;
|
||||||
|
el.src = '/images/no-vehicle-image.png';
|
||||||
|
},
|
||||||
|
|
||||||
|
onVehicleSelect(vehicle: IVehicle) {
|
||||||
|
if (this.store.chosenVehicle?.type === vehicle.type) this.addVehicle(vehicle);
|
||||||
|
this.previewVehicle(vehicle);
|
||||||
|
},
|
||||||
|
|
||||||
|
resetSearchInput() {
|
||||||
|
this.searchedVehicleTypeName = '';
|
||||||
|
},
|
||||||
|
|
||||||
|
filterVehicles(v: IVehicle) {
|
||||||
|
if (
|
||||||
|
this.searchedVehicleTypeName != '' &&
|
||||||
|
!v.type.toLocaleLowerCase().includes(this.searchedVehicleTypeName.toLocaleLowerCase())
|
||||||
|
)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
switch (this.filterType) {
|
||||||
|
case VehicleFilter.AllTractions:
|
||||||
|
return isTractionUnit(v);
|
||||||
|
|
||||||
|
case VehicleFilter.ElectricTraction:
|
||||||
|
return isTractionUnit(v) && v.group == 'loco-electric';
|
||||||
|
|
||||||
|
case VehicleFilter.DieselTraction:
|
||||||
|
return isTractionUnit(v) && v.group == 'loco-diesel';
|
||||||
|
|
||||||
|
case VehicleFilter.EmuTraction:
|
||||||
|
return isTractionUnit(v) && v.group == 'unit-electric';
|
||||||
|
|
||||||
|
case VehicleFilter.DmuTraction:
|
||||||
|
return isTractionUnit(v) && v.group == 'unit-diesel';
|
||||||
|
|
||||||
|
case VehicleFilter.AllWagons:
|
||||||
|
return !isTractionUnit(v);
|
||||||
|
|
||||||
|
case VehicleFilter.PassengerWagons:
|
||||||
|
return !isTractionUnit(v) && v.group == 'wagon-passenger';
|
||||||
|
|
||||||
|
case VehicleFilter.FreightWagons:
|
||||||
|
return !isTractionUnit(v) && v.group == 'wagon-freight';
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
sortVehicles(v1: IVehicle, v2: IVehicle) {
|
||||||
|
const direction = this.sorterDirection == 'asc' ? 1 : -1;
|
||||||
|
|
||||||
|
switch (this.sorterType) {
|
||||||
|
case 'type':
|
||||||
|
case 'group':
|
||||||
|
return direction * v1[this.sorterType].localeCompare(v2[this.sorterType]);
|
||||||
|
|
||||||
|
case 'weight':
|
||||||
|
case 'length':
|
||||||
|
case 'maxSpeed':
|
||||||
|
return Math.sign(v1[this.sorterType] - v2[this.sorterType]) * direction;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return v1.type.localeCompare(v2.type) * direction;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
computedVehicles() {
|
||||||
|
return this.store.vehicleDataList.filter(this.filterVehicles).sort(this.sortVehicles);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@use '@/styles/tab';
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(10em, 1fr));
|
||||||
|
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5em;
|
||||||
|
|
||||||
|
label {
|
||||||
|
color: #ccc;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.action.action-input label {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reset-btn {
|
||||||
|
display: flex;
|
||||||
|
background-color: #161c2e;
|
||||||
|
border-radius: 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vehicles {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
|
||||||
|
gap: 0.5em;
|
||||||
|
overflow: auto;
|
||||||
|
|
||||||
|
max-height: 730px;
|
||||||
|
|
||||||
|
margin-top: 0.75em;
|
||||||
|
padding: 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vehicles > li {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5em;
|
||||||
|
|
||||||
|
background-color: #161c2e;
|
||||||
|
padding: 0.5em;
|
||||||
|
|
||||||
|
min-height: 75px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&[data-preview='true'] {
|
||||||
|
background-color: #435288;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > span {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vehicle-name {
|
||||||
|
overflow: hidden;
|
||||||
|
text-wrap: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sponsor-only {
|
||||||
|
color: global.$sponsorColor;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: '*';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.team-only {
|
||||||
|
color: global.$teamColor;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: '*';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vehicle-props {
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-vehicles-warning {
|
||||||
|
text-align: center;
|
||||||
|
padding: 1em;
|
||||||
|
background-color: #161c2e;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: global.$breakpointSm) {
|
||||||
|
.actions-panel {
|
||||||
|
align-items: stretch;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions-panel_vehicles {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions-panel_search {
|
||||||
|
display: grid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,375 @@
|
|||||||
|
<template>
|
||||||
|
<div class="stock_actions">
|
||||||
|
<div class="actions-top">
|
||||||
|
<button
|
||||||
|
class="btn btn--image"
|
||||||
|
@click="clickFileInput"
|
||||||
|
:data-button-tooltip="$t('stocklist.action-upload-file')"
|
||||||
|
>
|
||||||
|
<input type="file" @change="uploadStockFromFile" ref="conFile" accept=".con,.txt" />
|
||||||
|
<FolderUp :stroke-width="2.5" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="btn btn--image"
|
||||||
|
@click="uploadStockFromClipboard"
|
||||||
|
:data-button-tooltip="$t('stocklist.action-upload-clipboard')"
|
||||||
|
>
|
||||||
|
<ClipboardPaste :stroke-width="2.5" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="btn btn--image"
|
||||||
|
:data-disabled="stockIsEmpty"
|
||||||
|
:disabled="stockIsEmpty"
|
||||||
|
@click="downloadStock"
|
||||||
|
:data-button-tooltip="$t('stocklist.action-download')"
|
||||||
|
>
|
||||||
|
<FolderDown :stroke-width="2.5" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="btn btn--image"
|
||||||
|
:data-disabled="stockIsEmpty"
|
||||||
|
:disabled="stockIsEmpty"
|
||||||
|
@click="copyToClipboard"
|
||||||
|
:data-button-tooltip="$t('stocklist.action-copy')"
|
||||||
|
>
|
||||||
|
<ClipboardCopy :stroke-width="2.5" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="btn btn--image"
|
||||||
|
:data-disabled="stockIsEmpty"
|
||||||
|
:disabled="stockIsEmpty"
|
||||||
|
@click="saveStockDataToStorage"
|
||||||
|
:data-button-tooltip="$t('stocklist.action-bookmark')"
|
||||||
|
>
|
||||||
|
<Bookmark :stroke-width="2.5" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="btn btn--image"
|
||||||
|
:data-disabled="stockIsEmpty"
|
||||||
|
:disabled="stockIsEmpty"
|
||||||
|
@click="resetStock"
|
||||||
|
:data-button-tooltip="$t('stocklist.action-reset')"
|
||||||
|
>
|
||||||
|
<ListRestart :stroke-width="2.5" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="btn btn--image"
|
||||||
|
:data-disabled="stockIsEmpty"
|
||||||
|
:disabled="stockIsEmpty"
|
||||||
|
@click="shuffleCars"
|
||||||
|
:data-button-tooltip="$t('stocklist.action-shuffle')"
|
||||||
|
>
|
||||||
|
<Shuffle :stroke-width="2.5" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="btn btn--image"
|
||||||
|
:data-disabled="stockIsEmpty"
|
||||||
|
:disabled="stockIsEmpty"
|
||||||
|
@click="turnAroundCars"
|
||||||
|
:data-button-tooltip="$t('stocklist.action-switch')"
|
||||||
|
>
|
||||||
|
<Repeat :stroke-width="2.5" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="actions-bottom" :data-disabled="store.chosenStockListIndex == -1">
|
||||||
|
<button
|
||||||
|
class="btn btn--image"
|
||||||
|
:tabindex="store.chosenStockListIndex == -1 ? -1 : 0"
|
||||||
|
@click="stockListUtils.moveUpStock(store.chosenStockListIndex)"
|
||||||
|
>
|
||||||
|
<ChevronUp :stroke-width="2.5" />
|
||||||
|
{{ $t('stocklist.action-move-up') }}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="btn btn--image"
|
||||||
|
:tabindex="store.chosenStockListIndex == -1 ? -1 : 0"
|
||||||
|
@click="stockListUtils.moveDownStock(store.chosenStockListIndex)"
|
||||||
|
>
|
||||||
|
<ChevronDown :stroke-width="2.5" />
|
||||||
|
{{ $t('stocklist.action-move-down') }}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="btn btn--image"
|
||||||
|
:tabindex="store.chosenStockListIndex == -1 ? -1 : 0"
|
||||||
|
@click="stockListUtils.removeStock(store.chosenStockListIndex)"
|
||||||
|
>
|
||||||
|
<Trash2 :stroke-width="2.5" />
|
||||||
|
{{ $t('stocklist.action-remove') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from 'vue';
|
||||||
|
import { useStore } from '../../../store';
|
||||||
|
|
||||||
|
import { isTractionUnit } from '../../../utils/vehicleUtils';
|
||||||
|
import { useFileUtils } from '../../../utils/fileUtils';
|
||||||
|
import stockMixin from '../../../mixins/stockMixin';
|
||||||
|
import { useStockListUtils } from '../../../utils/stockListUtils';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Bookmark,
|
||||||
|
ChevronDown,
|
||||||
|
ChevronUp,
|
||||||
|
ClipboardCopy,
|
||||||
|
ClipboardPaste,
|
||||||
|
Download,
|
||||||
|
FolderDown,
|
||||||
|
FolderUp,
|
||||||
|
ListRestart,
|
||||||
|
Repeat,
|
||||||
|
Shuffle,
|
||||||
|
Trash2,
|
||||||
|
} from 'lucide-vue-next';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
mixins: [stockMixin],
|
||||||
|
|
||||||
|
components: {
|
||||||
|
Bookmark,
|
||||||
|
ChevronDown,
|
||||||
|
ChevronUp,
|
||||||
|
ClipboardCopy,
|
||||||
|
ClipboardPaste,
|
||||||
|
Download,
|
||||||
|
FolderUp,
|
||||||
|
ListRestart,
|
||||||
|
Repeat,
|
||||||
|
Shuffle,
|
||||||
|
FolderDown,
|
||||||
|
Trash2,
|
||||||
|
},
|
||||||
|
|
||||||
|
data: () => ({
|
||||||
|
store: useStore(),
|
||||||
|
}),
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
const fileUtils = useFileUtils();
|
||||||
|
const stockListUtils = useStockListUtils();
|
||||||
|
|
||||||
|
return {
|
||||||
|
fileUtils,
|
||||||
|
stockListUtils,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
stockIsEmpty() {
|
||||||
|
return this.store.stockList.length == 0;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
copyToClipboard() {
|
||||||
|
navigator.clipboard.writeText(this.store.stockString);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
alert(this.$t('stocklist.alert-copied'));
|
||||||
|
}, 20);
|
||||||
|
},
|
||||||
|
|
||||||
|
clickFileInput() {
|
||||||
|
(this.$refs['conFile'] as HTMLInputElement).click();
|
||||||
|
},
|
||||||
|
|
||||||
|
resetStock() {
|
||||||
|
this.store.stockList.length = 0;
|
||||||
|
this.store.chosenStockListIndex = -1;
|
||||||
|
},
|
||||||
|
|
||||||
|
shuffleCars() {
|
||||||
|
const availableIndexes = this.store.stockList.reduce((acc, stock, i) => {
|
||||||
|
if (!isTractionUnit(stock.vehicleRef)) acc.push(i);
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, [] as number[]);
|
||||||
|
|
||||||
|
for (let i = 0; i < this.store.stockList.length; i++) {
|
||||||
|
if (!availableIndexes.includes(i)) continue;
|
||||||
|
|
||||||
|
availableIndexes.splice(i, -1);
|
||||||
|
|
||||||
|
const randAvailableIndex =
|
||||||
|
availableIndexes[Math.floor(Math.random() * availableIndexes.length)];
|
||||||
|
const tempSwap = this.store.stockList[randAvailableIndex];
|
||||||
|
|
||||||
|
this.store.stockList[randAvailableIndex] = this.store.stockList[i];
|
||||||
|
this.store.stockList[i] = tempSwap;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
turnAroundCars() {
|
||||||
|
if (this.store.stockList.length <= 1) return;
|
||||||
|
|
||||||
|
const isFirstTractionUnit = isTractionUnit(this.store.stockList[0].vehicleRef);
|
||||||
|
|
||||||
|
const sliceToSwap = isFirstTractionUnit
|
||||||
|
? this.store.stockList.slice(1)
|
||||||
|
: this.store.stockList.slice();
|
||||||
|
|
||||||
|
sliceToSwap.reverse();
|
||||||
|
|
||||||
|
if (isFirstTractionUnit) sliceToSwap.unshift(this.store.stockList[0]);
|
||||||
|
|
||||||
|
this.store.stockList = sliceToSwap;
|
||||||
|
},
|
||||||
|
|
||||||
|
downloadStock() {
|
||||||
|
if (this.store.stockList.length == 0) return alert(this.$t('stocklist.alert-empty'));
|
||||||
|
|
||||||
|
const defaultName = this.fileUtils.getCurrentStockFileName();
|
||||||
|
const fileName = prompt(this.$t('stocklist.prompt-file'), defaultName);
|
||||||
|
|
||||||
|
if (!fileName) return;
|
||||||
|
|
||||||
|
const blob = new Blob([this.store.stockString]);
|
||||||
|
const file = fileName + '.con';
|
||||||
|
|
||||||
|
var e = document.createEvent('MouseEvents'),
|
||||||
|
a = document.createElement('a');
|
||||||
|
a.download = file;
|
||||||
|
a.href = window.URL.createObjectURL(blob);
|
||||||
|
a.dataset.downloadurl = ['', a.download, a.href].join(':');
|
||||||
|
e.initEvent('click', true, false);
|
||||||
|
a.dispatchEvent(e);
|
||||||
|
},
|
||||||
|
|
||||||
|
uploadStockFromFile() {
|
||||||
|
const inputEl = this.$refs['conFile'] as HTMLInputElement;
|
||||||
|
const files = inputEl.files;
|
||||||
|
|
||||||
|
if (files?.length != 1) return;
|
||||||
|
if (!/\.con$/.test(files[0].name)) return;
|
||||||
|
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.readAsText(files[0]);
|
||||||
|
|
||||||
|
reader.onload = (res) => {
|
||||||
|
const stockString = res.target?.result;
|
||||||
|
|
||||||
|
if (!stockString || typeof stockString !== 'string') return;
|
||||||
|
|
||||||
|
this.loadStockFromString(stockString);
|
||||||
|
};
|
||||||
|
|
||||||
|
reader.onerror = (err) => console.error(err);
|
||||||
|
|
||||||
|
inputEl.value = '';
|
||||||
|
},
|
||||||
|
|
||||||
|
saveStockDataToStorage() {
|
||||||
|
if (this.store.stockList.length == 0) return;
|
||||||
|
|
||||||
|
const defaultName = this.fileUtils.getCurrentStockFileName();
|
||||||
|
const entryName = prompt(this.$t('stocklist.prompt-bookmark'), defaultName);
|
||||||
|
|
||||||
|
if (!entryName) return;
|
||||||
|
|
||||||
|
if (entryName in this.store.storageStockData) {
|
||||||
|
const overwriteDataConfirm = confirm(this.$t('stocklist.prompt-bookmark-overwrite'));
|
||||||
|
|
||||||
|
if (!overwriteDataConfirm) return;
|
||||||
|
|
||||||
|
this.store.storageStockData[entryName]['updatedAt'] = Date.now();
|
||||||
|
this.store.storageStockData[entryName]['stockString'] = this.store.stockString;
|
||||||
|
} else {
|
||||||
|
this.store.storageStockData[entryName] = {
|
||||||
|
id: entryName,
|
||||||
|
createdAt: Date.now(),
|
||||||
|
stockString: this.store.stockString,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
localStorage.setItem('savedStockData', JSON.stringify(this.store.storageStockData));
|
||||||
|
this.store.chosenStorageStockName = entryName;
|
||||||
|
this.store.chosenStorageStockString = this.store.stockString;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Wystąpił błąd podczas zapisywania składu do localStorage!', error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async uploadStockFromClipboard() {
|
||||||
|
try {
|
||||||
|
const content = await navigator.clipboard.readText();
|
||||||
|
this.loadStockFromString(content);
|
||||||
|
} catch (error) {
|
||||||
|
switch (error) {
|
||||||
|
case 'stock-loading-error':
|
||||||
|
alert(this.$t('stocklist.stock-loading-error'));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
alert(this.$t('stocklist.stock-clipboard-error'));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.stock_actions {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions-bottom {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
gap: 0.5em;
|
||||||
|
|
||||||
|
padding: 0.5em;
|
||||||
|
|
||||||
|
background-color: #353a57;
|
||||||
|
|
||||||
|
&[data-disabled='true'] {
|
||||||
|
opacity: 0.8;
|
||||||
|
|
||||||
|
user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions-top {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.5em;
|
||||||
|
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
|
||||||
|
|
||||||
|
button {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
&[data-button-tooltip] {
|
||||||
|
font-size: 0.85em;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
opacity: 0;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,261 @@
|
|||||||
|
<template>
|
||||||
|
<div class="list-wrapper">
|
||||||
|
<StockThumbnails :onListItemClick="onListItemClick" />
|
||||||
|
|
||||||
|
<div v-if="stockIsEmpty" class="list-empty">
|
||||||
|
<div class="stock-info">{{ $t('stocklist.list-empty') }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ul v-else>
|
||||||
|
<transition-group name="stock-list-anim">
|
||||||
|
<li
|
||||||
|
v-for="(stock, i) in store.stockList"
|
||||||
|
:key="stock.id"
|
||||||
|
:class="{ loco: isTractionUnit(stock.vehicleRef) }"
|
||||||
|
tabindex="0"
|
||||||
|
@click="onListItemClick(i)"
|
||||||
|
@keydown.enter="onListItemClick(i)"
|
||||||
|
@keydown.w="stockListUtils.moveUpStock(i)"
|
||||||
|
@keydown.s="stockListUtils.moveDownStock(i)"
|
||||||
|
@keydown.backspace="stockListUtils.removeStock(i)"
|
||||||
|
ref="itemRefs"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="stock-info"
|
||||||
|
@dragstart="onDragStart(i)"
|
||||||
|
@drop="onDrop($event, i)"
|
||||||
|
@dragover="allowDrop"
|
||||||
|
draggable="true"
|
||||||
|
>
|
||||||
|
<span class="stock-info-no" :data-selected="i == store.chosenStockListIndex">
|
||||||
|
<span v-if="i == store.chosenStockListIndex">• </span>
|
||||||
|
{{ i + 1 }}.
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span
|
||||||
|
class="stock-info-type"
|
||||||
|
:data-sponsor-only="
|
||||||
|
stock.vehicleRef.sponsorOnlyTimestamp &&
|
||||||
|
stock.vehicleRef.sponsorOnlyTimestamp > Date.now()
|
||||||
|
"
|
||||||
|
:data-team-only="stock.vehicleRef.teamOnly"
|
||||||
|
>
|
||||||
|
{{
|
||||||
|
isTractionUnit(stock.vehicleRef)
|
||||||
|
? stock.vehicleRef.type
|
||||||
|
: getCarSpecFromType(stock.vehicleRef.type)
|
||||||
|
}}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span class="stock-info-cargo" v-if="stock.cargo">
|
||||||
|
{{ stock.cargo.id }}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span class="stock-info-length">{{ stock.vehicleRef.length }}m</span>
|
||||||
|
|
||||||
|
<span class="stock-info-mass">
|
||||||
|
{{ ((stock.vehicleRef.weight + (stock.cargo?.weight ?? 0)) / 1000).toFixed(1) }}t
|
||||||
|
</span>
|
||||||
|
<span class="stock-info-speed">{{ stock.vehicleRef.maxSpeed }}km/h</span>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</transition-group>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from 'vue';
|
||||||
|
import { useStore } from '../../../store';
|
||||||
|
import { isTractionUnit } from '../../../utils/vehicleUtils';
|
||||||
|
import { useStockListUtils } from '../../../utils/stockListUtils';
|
||||||
|
import stockPreviewMixin from '../../../mixins/stockPreviewMixin';
|
||||||
|
import StockThumbnails from './StockThumbnails.vue';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
mixins: [stockPreviewMixin],
|
||||||
|
components: { StockThumbnails },
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
const stockListUtils = useStockListUtils();
|
||||||
|
|
||||||
|
return { stockListUtils };
|
||||||
|
},
|
||||||
|
|
||||||
|
data: () => ({
|
||||||
|
store: useStore(),
|
||||||
|
draggedVehicleID: -1,
|
||||||
|
}),
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
stockIsEmpty() {
|
||||||
|
return this.store.stockList.length == 0;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
isTractionUnit,
|
||||||
|
|
||||||
|
onDragStart(vehicleIndex: number) {
|
||||||
|
this.draggedVehicleID = vehicleIndex;
|
||||||
|
},
|
||||||
|
|
||||||
|
onDrop(e: DragEvent, vehicleIndex: number) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
let targetEl = (this.$refs['itemRefs'] as Element[])[vehicleIndex];
|
||||||
|
|
||||||
|
if (!targetEl) return;
|
||||||
|
|
||||||
|
const tempVehicle = this.store.stockList[vehicleIndex];
|
||||||
|
|
||||||
|
this.store.stockList[vehicleIndex] = this.store.stockList[this.draggedVehicleID];
|
||||||
|
this.store.stockList[this.draggedVehicleID] = tempVehicle;
|
||||||
|
|
||||||
|
this.store.chosenStockListIndex = vehicleIndex;
|
||||||
|
},
|
||||||
|
|
||||||
|
allowDrop(e: DragEvent) {
|
||||||
|
e.preventDefault();
|
||||||
|
},
|
||||||
|
|
||||||
|
onListItemClick(stockID: number) {
|
||||||
|
const stock = this.store.stockList[stockID];
|
||||||
|
|
||||||
|
this.store.chosenStockListIndex =
|
||||||
|
this.store.chosenStockListIndex == stockID &&
|
||||||
|
this.store.chosenVehicle?.type == stock.vehicleRef.type
|
||||||
|
? -1
|
||||||
|
: stockID;
|
||||||
|
|
||||||
|
if (this.store.chosenStockListIndex == -1) {
|
||||||
|
this.store.chosenVehicle = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.store.swapVehicles) this.store.swapVehicles = false;
|
||||||
|
|
||||||
|
this.previewStock(stock);
|
||||||
|
},
|
||||||
|
|
||||||
|
getCarSpecFromType(typeStr: string) {
|
||||||
|
const specArray = typeStr.split('_');
|
||||||
|
|
||||||
|
if (specArray.length == 0) return null;
|
||||||
|
|
||||||
|
/* 111a_Grafitti_1 */
|
||||||
|
if (specArray.length == 3) return `${specArray[0]} ${specArray[1]}-${specArray[2]}`;
|
||||||
|
|
||||||
|
/* 111a_PKP_Bnouz_01 */
|
||||||
|
return `${specArray[0]} ${specArray[2]}-${specArray[3]} (${specArray[1]})`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.list-wrapper {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-empty {
|
||||||
|
background-color: global.$secondaryColor;
|
||||||
|
border-radius: 0.5em;
|
||||||
|
padding: 0.75em;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
overflow-y: scroll;
|
||||||
|
height: 500px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul > li {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
min-width: 500px;
|
||||||
|
|
||||||
|
margin: 0.25em 0;
|
||||||
|
|
||||||
|
outline: none;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:focus-visible {
|
||||||
|
outline: 1px solid white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
li > .stock-info {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.25em;
|
||||||
|
|
||||||
|
color: white;
|
||||||
|
font-weight: 700;
|
||||||
|
|
||||||
|
transition: color 100ms;
|
||||||
|
|
||||||
|
& > span {
|
||||||
|
padding: 0.5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.stock-info-no,
|
||||||
|
.stock-info-type {
|
||||||
|
background-color: global.$secondaryColor;
|
||||||
|
|
||||||
|
&[data-team-only='true'] {
|
||||||
|
color: global.$teamColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-sponsor-only='true'] {
|
||||||
|
color: global.$sponsorColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.stock-info-no {
|
||||||
|
min-width: 3.5em;
|
||||||
|
text-align: right;
|
||||||
|
|
||||||
|
&[data-selected='true'] {
|
||||||
|
color: global.$accentColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.stock-info-cargo {
|
||||||
|
background-color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stock-info-length,
|
||||||
|
.stock-info-mass,
|
||||||
|
.stock-info-speed {
|
||||||
|
background-color: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stock-list-anim {
|
||||||
|
&-move, /* apply transition to moving elements */
|
||||||
|
&-enter-active,
|
||||||
|
&-leave-active {
|
||||||
|
transition: all 250ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-enter-from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(-25px);
|
||||||
|
}
|
||||||
|
|
||||||
|
&-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-leave-active {
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: global.$breakpointMd) {
|
||||||
|
ul {
|
||||||
|
min-height: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
<template>
|
||||||
|
<div class="stock_spawn-settings">
|
||||||
|
<Checkbox :disabled="!store.stockSupportsColdStart" v-model="store.isColdStart">
|
||||||
|
{{ $t('stocklist.coldstart-info') }}
|
||||||
|
</Checkbox>
|
||||||
|
|
||||||
|
<Checkbox :disabled="!store.stockSupportsDoubleManning" v-model="store.isDoubleManned">
|
||||||
|
{{ $t('stocklist.doublemanning-info') }}
|
||||||
|
</Checkbox>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from 'vue';
|
||||||
|
import { useStore } from '../../../store';
|
||||||
|
import Checkbox from '../../common/Checkbox.vue';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
components: { Checkbox },
|
||||||
|
|
||||||
|
data: () => ({
|
||||||
|
store: useStore(),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.stock_spawn-settings {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
<template>
|
||||||
|
<div class="stock-specs">
|
||||||
|
<div v-if="store.chosenStorageStockName || chosenRealComposition">
|
||||||
|
<b v-if="store.chosenStorageStockName">
|
||||||
|
<span
|
||||||
|
class="text--accent"
|
||||||
|
:title="store.chosenStorageStockName.length > 41 ? store.chosenStorageStockName : ''"
|
||||||
|
>
|
||||||
|
<BookmarkCheck :size="19" />
|
||||||
|
{{ store.chosenStorageStockName.slice(0, 40) }}
|
||||||
|
{{ store.chosenStorageStockName.length > 41 ? '...' : '' }}
|
||||||
|
</span>
|
||||||
|
</b>
|
||||||
|
|
||||||
|
<span v-if="store.chosenStorageStockName && chosenRealComposition"> | </span>
|
||||||
|
|
||||||
|
<b class="real-stock-info" v-if="chosenRealComposition">
|
||||||
|
<span class="text--accent">
|
||||||
|
<img
|
||||||
|
class="real-stock-icon"
|
||||||
|
:src="getIconURL(chosenRealComposition.type)"
|
||||||
|
:alt="chosenRealComposition.type"
|
||||||
|
/>
|
||||||
|
{{ chosenRealComposition.number }} {{ chosenRealComposition.name }}
|
||||||
|
</span>
|
||||||
|
</b>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<span>
|
||||||
|
{{ $t('stocklist.mass') }}
|
||||||
|
<span class="text--accent">{{ (store.totalWeight / 1000).toFixed(1) }}t</span>
|
||||||
|
({{ $t('stocklist.mass-accepted') }}:
|
||||||
|
<span class="text--accent">{{
|
||||||
|
store.acceptableWeight ? `${~~(store.acceptableWeight / 1000)}t` : '-'
|
||||||
|
}}</span
|
||||||
|
>) - {{ $t('stocklist.length') }}:
|
||||||
|
<span class="text--accent">{{ store.totalLength }}m</span>
|
||||||
|
- {{ $t('stocklist.vmax') }}
|
||||||
|
<span tabindex="0" :data-tooltip="$t('stocklist.disclaimer')">(?)</span>:
|
||||||
|
<span class="text--accent">{{ store.maxStockSpeed }} km/h</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from 'vue';
|
||||||
|
import { useStore } from '../../../store';
|
||||||
|
import imageMixin from '../../../mixins/imageMixin';
|
||||||
|
import { BookmarkCheck } from 'lucide-vue-next';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
components: { BookmarkCheck },
|
||||||
|
|
||||||
|
mixins: [imageMixin],
|
||||||
|
|
||||||
|
data: () => ({
|
||||||
|
store: useStore(),
|
||||||
|
}),
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
chosenRealComposition() {
|
||||||
|
const currentStockString = this.store.stockList.map((s) => s.vehicleRef.type).join(';');
|
||||||
|
|
||||||
|
return this.store.realCompositionList.find((rc) => rc.stockString == currentStockString);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.real-stock-icon {
|
||||||
|
height: 1.3ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stock-specs svg {
|
||||||
|
vertical-align: text-top;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,228 @@
|
|||||||
|
<template>
|
||||||
|
<div class="stock-thumbnails" ref="thumbnailsRef">
|
||||||
|
<ul>
|
||||||
|
<li
|
||||||
|
class="thumbnail-item"
|
||||||
|
v-for="(stock, stockIndex) in store.stockList"
|
||||||
|
:key="stockIndex"
|
||||||
|
:data-selected="store.chosenStockListIndex == stockIndex"
|
||||||
|
:data-sponsor-only="
|
||||||
|
stock.vehicleRef.sponsorOnlyTimestamp &&
|
||||||
|
stock.vehicleRef.sponsorOnlyTimestamp > Date.now()
|
||||||
|
"
|
||||||
|
:data-team-only="stock.vehicleRef.teamOnly"
|
||||||
|
draggable="true"
|
||||||
|
@dragstart="onDragStart(stockIndex)"
|
||||||
|
@drop="onDrop($event, stockIndex)"
|
||||||
|
@dragover="allowDrop"
|
||||||
|
@click="onListItemClick(stockIndex)"
|
||||||
|
>
|
||||||
|
<div class="stock-text">
|
||||||
|
<p>
|
||||||
|
{{ stock.vehicleRef.type.replace(/_/g, ' ') }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<span>
|
||||||
|
<img
|
||||||
|
v-for="thumbnail in getVehicleThumbnails(stock.vehicleRef.type)"
|
||||||
|
draggable="false"
|
||||||
|
style="min-width: 200px"
|
||||||
|
:src="`https://stacjownik.spythere.eu/static/thumbnails/${thumbnail.src}.png`"
|
||||||
|
:alt="stock.vehicleRef.type"
|
||||||
|
:title="stock.vehicleRef.type"
|
||||||
|
@load="($event) => (($event.target as HTMLImageElement).style.minWidth = 'auto')"
|
||||||
|
@error="
|
||||||
|
($event) =>
|
||||||
|
(($event.target as HTMLImageElement).src = `/images/${thumbnail.fallbackSrc}.png`)
|
||||||
|
"
|
||||||
|
height="70"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Ref, computed, nextTick, ref, watch } from 'vue';
|
||||||
|
import { useStore } from '../../../store';
|
||||||
|
|
||||||
|
const store = useStore();
|
||||||
|
const emit = defineEmits(['listItemClick']);
|
||||||
|
|
||||||
|
const thumbnailsRef = ref() as Ref<HTMLElement>;
|
||||||
|
const draggedIndex = ref(-1);
|
||||||
|
|
||||||
|
const onListItemClick = (index: number) => {
|
||||||
|
emit('listItemClick', index);
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(
|
||||||
|
computed(() => store.chosenStockListIndex),
|
||||||
|
(index) => {
|
||||||
|
if (index < 0) return;
|
||||||
|
|
||||||
|
nextTick(() => {
|
||||||
|
(thumbnailsRef.value as HTMLElement)
|
||||||
|
.querySelector(`li:nth-child(${index + 1})`)
|
||||||
|
?.scrollIntoView({
|
||||||
|
block: 'nearest',
|
||||||
|
inline: 'start',
|
||||||
|
behavior: 'smooth',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Dragging images
|
||||||
|
const onDragStart = (vehicleIndex: number) => {
|
||||||
|
draggedIndex.value = vehicleIndex;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onDrop = (e: DragEvent, vehicleIndex: number) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
let targetEl = thumbnailsRef.value.querySelector(`li:nth-child(${vehicleIndex + 1})`);
|
||||||
|
|
||||||
|
if (!targetEl && draggedIndex.value != -1) return;
|
||||||
|
|
||||||
|
const tempVehicle = store.stockList[vehicleIndex];
|
||||||
|
store.stockList[vehicleIndex] = store.stockList[draggedIndex.value];
|
||||||
|
store.stockList[draggedIndex.value] = tempVehicle;
|
||||||
|
|
||||||
|
store.chosenStockListIndex = vehicleIndex;
|
||||||
|
};
|
||||||
|
|
||||||
|
const allowDrop = (e: DragEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
};
|
||||||
|
|
||||||
|
const getVehicleThumbnails = (vehicleString: string) => {
|
||||||
|
const [vehicleName, vehicleCargo] = vehicleString.split(':');
|
||||||
|
|
||||||
|
const thumbnails: { src: string; fallbackSrc: string }[] = [];
|
||||||
|
|
||||||
|
// Generowanie członów EN57
|
||||||
|
if (vehicleName.startsWith('EN57')) {
|
||||||
|
thumbnails.push(
|
||||||
|
{ src: vehicleName + 'ra', fallbackSrc: 'unknown_ezt-ra' },
|
||||||
|
{ src: vehicleName + 's', fallbackSrc: 'unknown_ezt-s' },
|
||||||
|
{ src: vehicleName + 'rb', fallbackSrc: 'unknown_ezt-rb' }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generowanie członów EN71
|
||||||
|
else if (vehicleName.startsWith('EN71')) {
|
||||||
|
thumbnails.push(
|
||||||
|
{ src: vehicleName + 'ra', fallbackSrc: 'unknown_ezt-ra' },
|
||||||
|
{ src: vehicleName + 'sa', fallbackSrc: 'unknown_ezt-sa' },
|
||||||
|
{ src: vehicleName + 'sb', fallbackSrc: 'unknown_ezt-sb' },
|
||||||
|
{ src: vehicleName + 'rb', fallbackSrc: 'unknown_ezt-rb' }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generowanie pojazdów i członów 2EN57
|
||||||
|
else if (vehicleString.startsWith('2EN57')) {
|
||||||
|
const [firstVehicleNumber, secondVehicleNumber] = vehicleString
|
||||||
|
.replace('2EN57-', '')
|
||||||
|
.split('+');
|
||||||
|
|
||||||
|
thumbnails.push(
|
||||||
|
{ src: `EN57-${firstVehicleNumber}ra`, fallbackSrc: 'unknown_ezt-ra' },
|
||||||
|
{ src: `EN57-${firstVehicleNumber}s`, fallbackSrc: 'unknown_ezt-s' },
|
||||||
|
{ src: `EN57-${firstVehicleNumber}rb`, fallbackSrc: 'unknown_ezt-rb' },
|
||||||
|
{ src: `EN57-${secondVehicleNumber}ra`, fallbackSrc: 'unknown_ezt-ra' },
|
||||||
|
{ src: `EN57-${secondVehicleNumber}s`, fallbackSrc: 'unknown_ezt-s' },
|
||||||
|
{ src: `EN57-${secondVehicleNumber}rb`, fallbackSrc: 'unknown_ezt-rb' }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generowanie członów Gor77
|
||||||
|
else if (vehicleString.startsWith('Gor77')) {
|
||||||
|
thumbnails.push(
|
||||||
|
{ src: vehicleName + '-A', fallbackSrc: 'unknown_Gor77-A' },
|
||||||
|
{ src: vehicleName + '-B', fallbackSrc: 'unknown_Gor77-B' },
|
||||||
|
{ src: vehicleName + '-C', fallbackSrc: 'unknown_Gor77-C' },
|
||||||
|
{ src: vehicleName + '-D', fallbackSrc: 'unknown_Gor77-D' }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generowanie członów ET41
|
||||||
|
else if (vehicleString.startsWith('ET41')) {
|
||||||
|
thumbnails.push(
|
||||||
|
{ src: vehicleName + '-A', fallbackSrc: 'unknown_ET41-A' },
|
||||||
|
{ src: vehicleName + '-B', fallbackSrc: 'unknown_ET41-B' }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generowanie pozostałych pojazdów
|
||||||
|
else {
|
||||||
|
let fallbackVehicleImage = 'unknown_cargo';
|
||||||
|
|
||||||
|
if (/^(EP|EU)/.test(vehicleName)) fallbackVehicleImage = 'unknown_train';
|
||||||
|
else if (/^(SM42)/.test(vehicleName)) fallbackVehicleImage = 'unknown_SM42';
|
||||||
|
else if (/(\d{3}a|(Bau|Gor)\d{2}|304C)_/.test(vehicleName))
|
||||||
|
fallbackVehicleImage = 'unknown_passenger';
|
||||||
|
|
||||||
|
thumbnails.push({ src: vehicleName, fallbackSrc: fallbackVehicleImage });
|
||||||
|
}
|
||||||
|
|
||||||
|
return thumbnails;
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.stock-thumbnails {
|
||||||
|
overflow: auto;
|
||||||
|
background-color: #353a57;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
min-height: 110px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thumbnail-item {
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 0.85em;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
|
||||||
|
&[data-selected='true'] {
|
||||||
|
background-color: rebeccapurple;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-sponsor-only='true'] > b {
|
||||||
|
color: global.$sponsorColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-team-only='true'] > b {
|
||||||
|
color: global.$teamColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
max-height: 70px;
|
||||||
|
width: auto;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.stock-text {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #ccc;
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thumbnail-item > span {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: flex-end;
|
||||||
|
cursor: crosshair;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
<template>
|
||||||
|
<div class="stock_warnings" v-if="hasAnyWarnings">
|
||||||
|
<div class="warning" v-if="locoNotSuitable">(!) {{ $t('stocklist.warning-not-suitable') }}</div>
|
||||||
|
|
||||||
|
<div class="warning" v-if="lengthExceeded && store.isTrainPassenger">
|
||||||
|
(!) {{ $t('stocklist.warning-passenger-too-long') }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="warning" v-if="lengthExceeded && !store.isTrainPassenger">
|
||||||
|
(!) {{ $t('stocklist.warning-freight-too-long') }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="warning" v-if="teamOnlyVehicles.length > 0">
|
||||||
|
(!)
|
||||||
|
{{
|
||||||
|
$t('stocklist.warning-team-only-vehicle', [
|
||||||
|
teamOnlyVehicles.map((v) => v.vehicleRef.type).join(', '),
|
||||||
|
])
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="warning" v-if="weightExceeded">
|
||||||
|
(!)
|
||||||
|
<i18n-t keypath="stocklist.warning-too-heavy">
|
||||||
|
<template #href>
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
href="https://docs.google.com/spreadsheets/d/1BvTU-U7huIaEheov22TrhTtROUM4MwVfdbq03GVAEM8"
|
||||||
|
>
|
||||||
|
{{ $t('stocklist.acceptable-mass-docs') }}
|
||||||
|
</a>
|
||||||
|
</template>
|
||||||
|
</i18n-t>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- <div class="warning" v-if="locoCountExceeded">
|
||||||
|
{{ $t('stocklist.warning-too-many-locos') }}
|
||||||
|
</div> -->
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from 'vue';
|
||||||
|
import { useStore } from '../../../store';
|
||||||
|
import { isTractionUnit } from '../../../utils/vehicleUtils';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
data: () => ({
|
||||||
|
store: useStore(),
|
||||||
|
}),
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
teamOnlyVehicles() {
|
||||||
|
return this.store.stockList.filter((stock) => stock.vehicleRef.teamOnly);
|
||||||
|
},
|
||||||
|
|
||||||
|
hasAnyWarnings() {
|
||||||
|
return (
|
||||||
|
this.weightExceeded || this.lengthExceeded || this.locoNotSuitable || this.teamOnlyVehicles
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
lengthExceeded() {
|
||||||
|
return (
|
||||||
|
(this.store.totalLength > 350 && this.store.isTrainPassenger) ||
|
||||||
|
(this.store.totalLength > 650 && !this.store.isTrainPassenger)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
weightExceeded() {
|
||||||
|
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) => isTractionUnit(stock.vehicleRef)) &&
|
||||||
|
this.store.stockList.some(
|
||||||
|
(stock) => isTractionUnit(stock.vehicleRef) && stock.vehicleRef.type.startsWith('EP')
|
||||||
|
)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.warning {
|
||||||
|
padding: 0.25em;
|
||||||
|
margin: 0.25em 0;
|
||||||
|
background: global.$accentColor;
|
||||||
|
color: black;
|
||||||
|
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: black;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="image-preview"
|
||||||
|
@click="store.vehiclePreviewSrc = ''"
|
||||||
|
@keydown.esc="store.vehiclePreviewSrc = ''"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<img :src="store.vehiclePreviewSrc" alt="preview" @error="onImageError" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from 'vue';
|
||||||
|
import { useStore } from '../../store';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
store: useStore(),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.$el.focus();
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
onImageError(event: Event) {
|
||||||
|
if(!event.target || !(event.target instanceof HTMLImageElement)) return
|
||||||
|
|
||||||
|
event.target.src = '/images/no-vehicle-image.png';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.image-preview {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 99;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
background: rgba(black, 0.85);
|
||||||
|
cursor: zoom-out;
|
||||||
|
|
||||||
|
img {
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
max-height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,124 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="stock_thumbnails" ref="thumbnailsRef">
|
|
||||||
<div
|
|
||||||
v-for="(stock, stockIndex) in store.stockList"
|
|
||||||
:data-selected="store.chosenStockListIndex == stockIndex"
|
|
||||||
draggable="true"
|
|
||||||
@dragstart="onDragStart(stockIndex)"
|
|
||||||
@drop="onDrop($event, stockIndex)"
|
|
||||||
@dragover="allowDrop"
|
|
||||||
>
|
|
||||||
<span @click="onListItemClick(stockIndex)" :key="stock.id">
|
|
||||||
<b>
|
|
||||||
{{ stock.type }}
|
|
||||||
</b>
|
|
||||||
|
|
||||||
<span>
|
|
||||||
<img
|
|
||||||
draggable="false"
|
|
||||||
:src="`https://rj.td2.info.pl/dist/img/thumbnails/${stock.type}.png`"
|
|
||||||
:alt="stock.type"
|
|
||||||
:title="stock.type"
|
|
||||||
@error="stockImageError($event, stock)"
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { Ref, computed, nextTick, ref, watch } from 'vue';
|
|
||||||
import { useStore } from '../../store';
|
|
||||||
import { IStock } from '../../types';
|
|
||||||
|
|
||||||
const store = useStore();
|
|
||||||
const emit = defineEmits(['listItemClick', 'stockImageError']);
|
|
||||||
|
|
||||||
const thumbnailsRef = ref() as Ref<HTMLElement>;
|
|
||||||
const draggedIndex = ref(-1);
|
|
||||||
|
|
||||||
const onListItemClick = (index: number) => {
|
|
||||||
emit('listItemClick', index);
|
|
||||||
};
|
|
||||||
|
|
||||||
const stockImageError = (e: Event, stock: IStock) => {
|
|
||||||
emit('stockImageError', e, stock);
|
|
||||||
};
|
|
||||||
|
|
||||||
watch(
|
|
||||||
computed(() => store.chosenStockListIndex),
|
|
||||||
(index) => {
|
|
||||||
if (index < 0) return;
|
|
||||||
|
|
||||||
nextTick(() => {
|
|
||||||
(thumbnailsRef.value as HTMLElement)
|
|
||||||
.querySelector(`div:nth-child(${index + 1})`)
|
|
||||||
?.scrollIntoView({ block: 'nearest', inline: 'start', behavior: 'smooth' });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// Dragging images
|
|
||||||
const onDragStart = (vehicleIndex: number) => {
|
|
||||||
draggedIndex.value = vehicleIndex;
|
|
||||||
};
|
|
||||||
|
|
||||||
const onDrop = (e: DragEvent, vehicleIndex: number) => {
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
let targetEl = thumbnailsRef.value.querySelector(`div:nth-child(${vehicleIndex + 1})`);
|
|
||||||
|
|
||||||
if (!targetEl && draggedIndex.value != -1) return;
|
|
||||||
|
|
||||||
const tempVehicle = store.stockList[vehicleIndex];
|
|
||||||
store.stockList[vehicleIndex] = store.stockList[draggedIndex.value];
|
|
||||||
store.stockList[draggedIndex.value] = tempVehicle;
|
|
||||||
|
|
||||||
store.chosenStockListIndex = vehicleIndex;
|
|
||||||
};
|
|
||||||
|
|
||||||
const allowDrop = (e: DragEvent) => {
|
|
||||||
e.preventDefault();
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.stock_thumbnails {
|
|
||||||
display: flex;
|
|
||||||
margin: 1em 0;
|
|
||||||
|
|
||||||
overflow: auto;
|
|
||||||
|
|
||||||
background-color: #353a57;
|
|
||||||
|
|
||||||
> div {
|
|
||||||
display: flex;
|
|
||||||
align-items: flex-end;
|
|
||||||
cursor: pointer;
|
|
||||||
min-height: 100px;
|
|
||||||
|
|
||||||
&[data-selected='true'] {
|
|
||||||
background-color: rebeccapurple;
|
|
||||||
}
|
|
||||||
|
|
||||||
> span {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 0.5em;
|
|
||||||
padding: 0.5em 0;
|
|
||||||
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
font-size: 0.85em;
|
|
||||||
user-select: none;
|
|
||||||
-moz-user-select: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
max-height: 60px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"EU06": [650000, 2000000],
|
||||||
|
"EU07": [650000, 2000000],
|
||||||
|
"4E": [650000, 2000000],
|
||||||
|
"EU07E": [650000, 2000000],
|
||||||
|
"EP07": [650000, 650000],
|
||||||
|
"EP08": [650000, 650000],
|
||||||
|
"EP09": [800000, 800000],
|
||||||
|
"ET41": [700000, 4000000],
|
||||||
|
"SM42": [2400000, 2400000]
|
||||||
|
}
|
||||||
@@ -1,39 +1,64 @@
|
|||||||
{
|
{
|
||||||
"regionNumbers": {
|
"regionNumbers": {
|
||||||
"Warszawa": 1,
|
"Warszawa (1)": 1,
|
||||||
"Lublin": 2,
|
"Lublin (2)": 2,
|
||||||
"Kraków": 3,
|
"Kraków (3)": 3,
|
||||||
"Sosnowiec": 4,
|
"Sosnowiec (4)": 4,
|
||||||
"Gdańsk": 5,
|
"Gdańsk (5)": 5,
|
||||||
"Wrocław": 6,
|
"Wrocław (6)": 6,
|
||||||
"Poznań": 7,
|
"Poznań (7)": 7,
|
||||||
"Szczecin": 8,
|
"Szczecin (8)": 8,
|
||||||
"Rezerwa": 9
|
"Rezerwa (9)": 9
|
||||||
},
|
},
|
||||||
"sameRegions": {
|
"sameRegions": {
|
||||||
"Losowy": [
|
"Losowy": [
|
||||||
10, 11, 19, 91, 93, 97, 99, 20, 22, 29, 30, 33, 39, 40, 44, 49, 94, 50, 55, 59, 90, 95, 96, 66, 60, 69, 77, 70,
|
10, 11, 19, 91, 93, 97, 99, 20, 22, 29, 30, 33, 39, 40, 44, 49, 94, 50, 55, 59, 90, 95, 96,
|
||||||
79, 88, 80, 89, 92, 98
|
66, 60, 69, 77, 70, 79, 88, 80, 89, 92, 98
|
||||||
],
|
],
|
||||||
"Warszawa": [10, 11, 19, 91, 93, 97, 99],
|
"Warszawa (1)": [10, 11, 19, 91, 93, 97, 99],
|
||||||
"Lublin": [20, 22, 29],
|
"Lublin (2)": [20, 22, 29],
|
||||||
"Kraków": [30, 33, 39],
|
"Kraków (3)": [30, 33, 39],
|
||||||
"Sosnowiec": [40, 44, 49, 94],
|
"Sosnowiec (4)": [40, 44, 49, 94],
|
||||||
"Gdańsk": [50, 55, 59, 90, 95, 96],
|
"Gdańsk (5)": [50, 55, 59, 90, 95, 96],
|
||||||
"Wrocław": [66, 60, 69],
|
"Wrocław (6)": [66, 60, 69],
|
||||||
"Poznań": [77, 70, 79],
|
"Poznań (7)": [77, 70, 79],
|
||||||
"Szczecin": [88, 80],
|
"Szczecin (8)": [88, 80],
|
||||||
"Rezerwa": [89, 92, 98]
|
"Rezerwa (9)": [89, 92, 98]
|
||||||
},
|
},
|
||||||
"categories": {
|
"categoriesRules": {
|
||||||
"ekspres krajowy (EI)": "2:00-99:2",
|
"EI": [null, "00", "99"],
|
||||||
"międzywojewódzki pośpieszny (MP)": "2:050-169:3",
|
"EC": [null, "000", "049"],
|
||||||
"wojewódzki pośpieszny (RP)": "2:050-169:3",
|
"EN": [null, "000", "049"],
|
||||||
"wojewódzki osobowy (RO)": "2:200-999:3",
|
|
||||||
"próżny \"służbowy\" (PW)": "2:6;3:0-899:3",
|
"RO": [null, "200", "999"],
|
||||||
"towarowy do przewozów masowych (TM)": "2:4;3:0-899:3",
|
"RP": [null, "050", "169"],
|
||||||
"towarowy do obsługi stacji (TK)": "2:3;3:0-899:3",
|
"RM": [null, "200", "999"],
|
||||||
"lokomotywa luzem (LT)": "2:5;3:0-899:3"
|
"RA": [null, "200", "999"],
|
||||||
|
|
||||||
|
"MO": [null, "200", "999"],
|
||||||
|
"MP": [null, "050", "169"],
|
||||||
|
"MM": [null, "001", "049"],
|
||||||
|
"MH": [null, "170", "199"],
|
||||||
|
|
||||||
|
"PW": ["6", "000", "899"],
|
||||||
|
"PX": ["6", "000", "899"],
|
||||||
|
|
||||||
|
"TM": ["4", "000", "899"],
|
||||||
|
"TN": ["3", "000", "899"],
|
||||||
|
"TK": ["3", "000", "899"],
|
||||||
|
"TD": ["2", "000", "899"],
|
||||||
|
"TG": ["1", "000", "899"],
|
||||||
|
"TR": ["1", "000", "899"],
|
||||||
|
"TC": ["0", "000", "899"],
|
||||||
|
"TS": ["5", "000", "899"],
|
||||||
|
"TH": ["5", "000", "899"],
|
||||||
|
|
||||||
|
"LT": ["5", "000", "899"],
|
||||||
|
"LP": ["6", "000", "899"],
|
||||||
|
"LS": ["9", "000", "899"],
|
||||||
|
"LZ": ["9", "000", "899"],
|
||||||
|
|
||||||
|
"ZN": ["9", "000", "899"],
|
||||||
|
"ZU": ["9", "000", "899"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,53 +1,153 @@
|
|||||||
{
|
{
|
||||||
"EU07": {
|
"EU07": {
|
||||||
"passenger": {
|
"passenger": {
|
||||||
"650": 125
|
"650000": 125
|
||||||
},
|
},
|
||||||
"cargo": {
|
"cargo": {
|
||||||
"2000": 70
|
"2000000": 70
|
||||||
}
|
},
|
||||||
|
"none": 110
|
||||||
|
},
|
||||||
|
"4E": {
|
||||||
|
"passenger": {
|
||||||
|
"650000": 125
|
||||||
|
},
|
||||||
|
"cargo": {
|
||||||
|
"2000000": 70
|
||||||
|
},
|
||||||
|
"none": 110
|
||||||
|
},
|
||||||
|
"EU07E": {
|
||||||
|
"passenger": {
|
||||||
|
"650000": 125
|
||||||
|
},
|
||||||
|
"cargo": {
|
||||||
|
"2000000": 70
|
||||||
|
},
|
||||||
|
"none": 110
|
||||||
},
|
},
|
||||||
"EP07": {
|
"EP07": {
|
||||||
"passenger": {
|
"passenger": {
|
||||||
"650": 125
|
"650000": 125
|
||||||
},
|
},
|
||||||
"cargo": null
|
"cargo": null,
|
||||||
|
"none": 110
|
||||||
},
|
},
|
||||||
"EP08": {
|
"EP08": {
|
||||||
"passenger": {
|
"passenger": {
|
||||||
"650": 140
|
"650000": 140
|
||||||
},
|
},
|
||||||
"cargo": null
|
"cargo": null,
|
||||||
|
"none": 110
|
||||||
|
},
|
||||||
|
"EP09": {
|
||||||
|
"passenger": {
|
||||||
|
"650000": 160
|
||||||
|
},
|
||||||
|
"cargo": null,
|
||||||
|
"none": 160
|
||||||
|
},
|
||||||
|
"ET22": {
|
||||||
|
"passenger": {
|
||||||
|
"650000": 125
|
||||||
|
},
|
||||||
|
"cargo": {
|
||||||
|
"1200000": 100,
|
||||||
|
"3100000": 70
|
||||||
|
},
|
||||||
|
"none": 125
|
||||||
|
},
|
||||||
|
"201E": {
|
||||||
|
"passenger": {
|
||||||
|
"650000": 125
|
||||||
|
},
|
||||||
|
"cargo": {
|
||||||
|
"1200000": 100,
|
||||||
|
"3100000": 70
|
||||||
|
},
|
||||||
|
"none": 125
|
||||||
},
|
},
|
||||||
"ET41": {
|
"ET41": {
|
||||||
"passenger": {
|
"passenger": {
|
||||||
"700": 125
|
"700000": 125
|
||||||
},
|
},
|
||||||
"cargo": {
|
"cargo": {
|
||||||
"4000": 70
|
"4000000": 70
|
||||||
}
|
},
|
||||||
|
"none": 110
|
||||||
},
|
},
|
||||||
"SM42": {
|
"SM42": {
|
||||||
"passenger": {
|
"passenger": {
|
||||||
"95": 90,
|
"95000": 90,
|
||||||
"200": 80,
|
"200000": 80,
|
||||||
"300": 70,
|
"300000": 70,
|
||||||
"450": 60,
|
"450000": 60,
|
||||||
"750": 50,
|
"750000": 50,
|
||||||
"1130": 40,
|
"1130000": 40,
|
||||||
"1720": 30,
|
"1720000": 30,
|
||||||
"2400": 20
|
"2400000": 20
|
||||||
},
|
},
|
||||||
"cargo": {
|
"cargo": {
|
||||||
"95": 90,
|
"95000": 90,
|
||||||
"200": 80,
|
"200000": 80,
|
||||||
"300": 70,
|
"300000": 70,
|
||||||
"450": 60,
|
"450000": 60,
|
||||||
"750": 50,
|
"750000": 50,
|
||||||
"1130": 40,
|
"1130000": 40,
|
||||||
"1720": 30,
|
"1720000": 30,
|
||||||
"2400": 20
|
"2400000": 20
|
||||||
}
|
},
|
||||||
|
"none": 90
|
||||||
|
},
|
||||||
|
"M62": {
|
||||||
|
"passenger": {
|
||||||
|
"500000": 100,
|
||||||
|
"800000": 80,
|
||||||
|
"1200000": 60,
|
||||||
|
"2000000": 40,
|
||||||
|
"3000000": 20
|
||||||
|
},
|
||||||
|
"cargo": {
|
||||||
|
"500000": 100,
|
||||||
|
"800000": 80,
|
||||||
|
"1200000": 60,
|
||||||
|
"2000000": 40,
|
||||||
|
"3000000": 20
|
||||||
|
},
|
||||||
|
"none": 100
|
||||||
|
},
|
||||||
|
"ST44": {
|
||||||
|
"passenger": {
|
||||||
|
"500000": 100,
|
||||||
|
"800000": 80,
|
||||||
|
"1200000": 60,
|
||||||
|
"2000000": 40,
|
||||||
|
"3000000": 20
|
||||||
|
},
|
||||||
|
"cargo": {
|
||||||
|
"500000": 100,
|
||||||
|
"800000": 80,
|
||||||
|
"1200000": 60,
|
||||||
|
"2000000": 40,
|
||||||
|
"3000000": 20
|
||||||
|
},
|
||||||
|
"none": 100
|
||||||
|
},
|
||||||
|
"CTLR4C": {
|
||||||
|
"passenger": {
|
||||||
|
"500000": 100,
|
||||||
|
"800000": 80,
|
||||||
|
"1200000": 60,
|
||||||
|
"2000000": 40,
|
||||||
|
"3000000": 20
|
||||||
|
},
|
||||||
|
"cargo": {
|
||||||
|
"500000": 100,
|
||||||
|
"800000": 80,
|
||||||
|
"1200000": 60,
|
||||||
|
"2000000": 40,
|
||||||
|
"3000000": 20
|
||||||
|
},
|
||||||
|
"none": 100
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
{
|
||||||
|
"cargo": {
|
||||||
|
"kontenery": ["627Z:all", "412Z:all"],
|
||||||
|
"chłodnia": ["202Lc:all"],
|
||||||
|
"drobnica": ["426S:all", "208Kf:all", "401Ka_PKP_Gags:all", "401Ka_PKPC_Gags:all"],
|
||||||
|
"węgiel": ["412W:coal_01", "413S:coal_413S", "429W:coal_01", "401Zb:coal_02"],
|
||||||
|
"ruda": ["412W:ore_01", "401Zl:ore_35", "429W:ore_01"],
|
||||||
|
"piasek": [
|
||||||
|
"412W:sand_01",
|
||||||
|
"412W:sand_02",
|
||||||
|
"413S:sand_413S",
|
||||||
|
"401Zl:sand_30",
|
||||||
|
"429W:sand_01",
|
||||||
|
"429W:sand_02",
|
||||||
|
"401Zb:sand_03",
|
||||||
|
"418Va:sand_418V",
|
||||||
|
"418Vb:sand_418V"
|
||||||
|
],
|
||||||
|
"kreda": ["413S:chalk_413S"],
|
||||||
|
"kamień": [
|
||||||
|
"412W:stone_01",
|
||||||
|
"412W:stone_50",
|
||||||
|
"401Zl:stone_25",
|
||||||
|
"429W:stone_01",
|
||||||
|
"401Zb:stone_02",
|
||||||
|
"418Va:stone_418V",
|
||||||
|
"418Vb:stone_418V"
|
||||||
|
],
|
||||||
|
"złom": ["412W:scrap_01", "412W:scrap_02", "429W:scrap_01", "429W:scrap_02"],
|
||||||
|
"paliwo": ["29R_CTLL:all", "29R_PKP:all", "445Rb:all"],
|
||||||
|
"melasa": ["29R_PLPOL:all"],
|
||||||
|
"żwir": ["441V"],
|
||||||
|
"koła": ["424Z:wheels_01"],
|
||||||
|
"drewno": ["424Z:woods_01", "424Z:woods_02"],
|
||||||
|
"szyny": ["424Z:rails_01"],
|
||||||
|
"kable": ["424Z:cables_01", "24Z:cables_Ks", "401Ze:cables_02"],
|
||||||
|
"kruszywo": ["59WS:all"],
|
||||||
|
"techniczne": ["209c", "304Ca", "102a_PKPE", "401Ka_PKP_XGa:all"],
|
||||||
|
"poczta": ["211K:all"],
|
||||||
|
"cement": [
|
||||||
|
"408S:cement_4",
|
||||||
|
"206S_CEMET:cement_3",
|
||||||
|
"206S_SPEED:cement_3",
|
||||||
|
"220S_CEMET:cement_3"
|
||||||
|
],
|
||||||
|
"wapno": ["408S:lime_4", "206S_CEMET:lime_3", "206S_SPEED:lime_3", "220S_CEMET:lime_3"],
|
||||||
|
"soda": ["408S:soda_4", "206S_CEMET:soda_3", "206S_SPEED:soda_3", "220S_CEMET:soda_3"],
|
||||||
|
"pszenica": ["206Sh_PKP_Ugpps:wheat_3", "206Sh_PKPC_Ugpps:wheat_3"],
|
||||||
|
"kukurydza": ["206Sh_PKP_Ugpps:corn_3", "206Sh_PKPC_Ugpps:corn_3"],
|
||||||
|
"pasza": ["206Sh_PKP_Ugpps:forage_3", "206Sh_PKPC_Ugpps:forage_3"],
|
||||||
|
"pojazdy": ["426Z:tank_01", "426Z:truck_01", "426Z:vehicles_01"],
|
||||||
|
"karbid": ["421S:carbide_01"],
|
||||||
|
"wrażliwe": ["425S:all", "421S:carbide_01"],
|
||||||
|
"stal": ["401Ze:steel_01", "401Ze:steel_02"],
|
||||||
|
"gaz": ["WB117:all"]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,272 @@
|
|||||||
|
{
|
||||||
|
"EIC 45_71006_71007 Sprewa": "EP08-006v2;111a_PKPIC_B10ou_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;113a_PKPIC_WRbd_01;158a_PKPIC_A9mnopuz_01",
|
||||||
|
"IC 15100_15101 Słowiniec": "EP08-006v2;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B10ou_01;111a_PKPIC_B9nopuvz_02;158a_PKPIC_A9mnopuz_01;113a_PKPIC_WRbd_01;111a_PKPIC_B10ou_01;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B9nopuvz_01;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01",
|
||||||
|
"IC 15103 Podlasiak": "EP07-1039;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01",
|
||||||
|
"IC 15106_15107 Brda": "EP08-006v2;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B9nopuvz_01",
|
||||||
|
"IC 15155_15154 Lazur": "EP08-006v2;111a_PKPIC_B10ou_01;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B9nopuvz_02;158a_PKPIC_A9mnopuz_01;158a_PKPIC_A9mnopuz_01;111a_PKPIC_B10ou_01;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B9nopuvz_02",
|
||||||
|
"IC 16150_16151 Śnieżka": "EP08-006v2;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B10ou_01;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B9nopuvz_02;113a_PKPIC_WRbd_01;158a_PKPIC_A9mnopuz_01;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B9nopuvz_02",
|
||||||
|
"IC 17100_17101 Zamenhof": "EP08-006v2;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B10ou_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01",
|
||||||
|
"IC 17102_17103 Zielonogórzanin": "EP08-006v2;111a_PKPIC_B10ou_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01",
|
||||||
|
"IC 17104_17105 Warta": "EP08-006v2;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01",
|
||||||
|
"IC 18100_18101 Gałczyński": "EP08-006v2;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01",
|
||||||
|
"IC 18102_18103 Podlasiak": "EP08-006v2;111a_PKPIC_B10ou_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B9nopuvz_02",
|
||||||
|
"IC 18104_18105 Rybak": "EP07-1043;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B10ou_01;159a_PKPIC_B9mnopuz_01",
|
||||||
|
"IC 18106_18107 Mewa": "EP08-006v2;111a_PKPIC_B10ou_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B10ou_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B9nopuvz_02",
|
||||||
|
"IC 18112_18113 Noteć": "EP08-006v2;111a_PKPIC_B9nopuvz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01",
|
||||||
|
"IC 18153_18152 Parsęta": "EP08-006v2;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B10ou_01;158a_PKPIC_A9mnopuz_01;158a_PKPIC_A9mnopuz_01;111a_PKPIC_B10ou_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B9nopuvz_02",
|
||||||
|
"IC 19100_19101 Łodzianin": "EP08-006v2;111a_PKPIC_B10ou_01;111a_PKPIC_B9nopuvz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B9nopuvz_02;158a_PKPIC_A9mnopuz_01;158a_PKPIC_A9mnopuz_01;111a_PKPIC_B10ou_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B10ou_01;111a_PKPIC_B9nopuvz_01",
|
||||||
|
"IC 20100_20101 Hetman": "EP08-006v2;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B10ou_01",
|
||||||
|
"IC 21100_21101 Chełmianin": "EP08-006v2;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B9nopuvz_02",
|
||||||
|
"IC 22100_22101 Hetman": "EP08-006v2;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01",
|
||||||
|
"IC 23100_23101 Jagiełło": "EP07-544;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01",
|
||||||
|
"IC 24100_24101 Sztygar": "EP07-1009;111a_PKPIC_B10ou_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01",
|
||||||
|
"IC 24105 Morcinek": "EP07-1025;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;111a_PKPIC_B9nopuvz_01",
|
||||||
|
"IC 25100_25101 Kochanowski": "EP08-006v2;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01",
|
||||||
|
"IC 26100_26101 Hetman": "EP08-006v2;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01",
|
||||||
|
"IC 26102_26103 Bolko": "EP07-1054;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01",
|
||||||
|
"IC 26104_26105 Morcinek": "EP07-1039;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B10ou_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01",
|
||||||
|
"IC 27100_27101 Lubuszanin": "EP08-006v2;111a_PKPIC_B10ou_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B9nopuvz_02",
|
||||||
|
"IC 31104_31105 Witos": "EP07-356;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01",
|
||||||
|
"IC 31106_31107 San": "EP07-1056;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B10ou_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;111a_PKPIC_B9nopuvz_02",
|
||||||
|
"IC 31110_31111 Marszałek Piłsudski": "EP08-006v2;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B10ou_01;158a_PKPIC_A9mnopuz_01;111a_PKPIC_B9nopuvz_02",
|
||||||
|
"IC 32100_32101 Jagiełło": "EP07-1054;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;111a_PKPIC_B10ou_01",
|
||||||
|
"IC 35102_35103 Kochanowski": "EP08-006v2;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B9nopuvz_02",
|
||||||
|
"IC 35150_35151 Halny": "EP07-391;158a_PKPIC_A9mnopuz_01;158a_PKPIC_A9mnopuz_01;158a_PKPIC_A9mnopuz_01;158a_PKPIC_A9mnopuz_01;158a_PKPIC_A9mnopuz_01",
|
||||||
|
"IC 35202_35203 Przebojowy pociąg RMF FM": "EP08-006v2;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;113a_PKPIC_WRbd_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01",
|
||||||
|
"IC 36104_36105 Kossak": "EP07-1043;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;113a_PKPIC_WRbd_01;158a_PKPIC_A9mnopuz_01;111a_PKPIC_B9nopuvz_02",
|
||||||
|
"IC 38100_38101 Górski": "EP08-006v2;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B9nopuvz_02",
|
||||||
|
"IC 38102_38103 Osterwa": "EP07-1025;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01;113a_PKPIC_WRbd_01;158a_PKPIC_A9mnopuz_01;111a_PKPIC_B10ou_01",
|
||||||
|
"IC 38104_38105 Mehoffer": "EP08-006v2;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B9nopuvz_02",
|
||||||
|
"IC 38172_38173 Przemyślanin": "EP08-006v2;110a_PKPIC_Bcdu_01;110a_PKPIC_Bcdu_02;110a_PKPIC_Bcdu_02;110a_PKPIC_Bcdu_02;158a_PKPIC_A9mnopuz_01;113a_PKPIC_WRbd_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B10ou_01;158a_PKPIC_A9mnopuz_01;111a_PKPIC_B9nopuvz_02",
|
||||||
|
"IC 38192_38193 Podhalanin": "EP07-1043;158a_PKPIC_A9mnopuz_01;158a_PKPIC_A9mnopuz_01;158a_PKPIC_A9mnopuz_01;158a_PKPIC_A9mnopuz_01;158a_PKPIC_A9mnopuz_01;110a_PKPIC_Bcdu_02;110a_PKPIC_Bcdu_02",
|
||||||
|
"IC 42100_42101 Sztygar": "EP07-544;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B10ou_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01",
|
||||||
|
"IC 42104 Morcinek": "EP07-1002;111a_PKPIC_B10ou_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01",
|
||||||
|
"IC 48150_48151 Gwarek": "EP07-384;111a_PKPIC_B9nopuvz_02;158a_PKPIC_A9mnopuz_01;111a_PKPIC_B10ou_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B10ou_01;158a_PKPIC_A9mnopuz_01;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B9nopuvz_02",
|
||||||
|
"IC 50104_50105 Niegocin": "EP07-391;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B9nopuvz_02;158a_PKPIC_A9mnopuz_01;111a_PKPIC_B10ou_01",
|
||||||
|
"IC 51100_51101 Słowiniec": "EP08-006v2;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B10ou_01;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B9nopuvz_02;113a_PKPIC_WRbd_01;158a_PKPIC_A9mnopuz_01;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B10ou_01;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01",
|
||||||
|
"IC 51102 Podlasiak": "EP07-335;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B9nopuvz_02",
|
||||||
|
"IC 51106_51107 Brda": "EP08-006v2;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B10ou_01",
|
||||||
|
"IC 51155_51154 Lazur": "EP08-006v2;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B10ou_01;158a_PKPIC_A9mnopuz_01;158a_PKPIC_A9mnopuz_01;111a_PKPIC_B10ou_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B10ou_01",
|
||||||
|
"IC 52100_52101 Kochanowski": "EP08-006v2;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B10ou_01\n",
|
||||||
|
"IC 53102_53103 Kochanowski": "EP08-006v2;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B9nopuvz_02",
|
||||||
|
"IC 53150_53151 Halny": "EP07-1043;158a_PKPIC_A9mnopuz_01;158a_PKPIC_A9mnopuz_01;158a_PKPIC_A9mnopuz_01;158a_PKPIC_A9mnopuz_01;158a_PKPIC_A9mnopuz_01",
|
||||||
|
"IC 55104_55105 Niegocin": "EP07-1031;111a_PKPIC_B10ou_01;158a_PKPIC_A9mnopuz_01;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01",
|
||||||
|
"IC 56102_56103 Mamry": "EP07-1043;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;112a_PKPIC_A9ou_01;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B10ou_01",
|
||||||
|
"IC 56104_56105 Jeziorak": "EP07-1038;112a_PKPIC_A9ou_01;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B10ou_01;154a_PKPIC_B10mnouz_01;159a_PKPIC_B9mnopuz_01",
|
||||||
|
"IC 57100_57101 Ukiel": "EP07-1048;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B9nopuvz_02;154a_PKPIC_B10mnouz_01;159a_PKPIC_B9mnopuz_01",
|
||||||
|
"IC 57102_57103 Drwęca": "EP07-1043;159a_PKPIC_B9mnopuz_01;154a_PKPIC_B10mnouz_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B9nopuvz_02\n",
|
||||||
|
"IC 57104_57105 Bachus": "EP07-174;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B9nopuvz_02",
|
||||||
|
"IC 57106_57107 Lednica": "EP07-1025;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B9nopuvz_02",
|
||||||
|
"IC 57108_57109 Stoczniowiec": "EP07-335;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B9nopuvz_02;154a_PKPIC_B10mnouz_01;159a_PKPIC_B9mnopuz_01",
|
||||||
|
"IC 58100_58101 Albatros": "EP07-1039;154a_PKPIC_B10mnouz_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01",
|
||||||
|
"IC 58102_58103 Żuławy": "EP07-1038;154a_PKPIC_B10mnouz_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01",
|
||||||
|
"IC 58104_58105 Bryza": "EP07-1009;111a_PKPIC_B9nopuvz_02;154a_PKPIC_B10mnouz_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01\n",
|
||||||
|
"IC 58106_58107 Gryf": "EP07-1009;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B10ou_01",
|
||||||
|
"IC 58108_58109 Stańczyk": "EP07-444;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;111a_PKPIC_B9nopuvz_02",
|
||||||
|
"IC 60101 Mehoffer": "EP07-1025;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B10ou_01",
|
||||||
|
"IC 60151 Śnieżka": "SM42-506;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01",
|
||||||
|
"IC 61150_61151 Śnieżka": "EP08-006v2;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;113a_PKPIC_WRbd_01;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B10ou_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01",
|
||||||
|
"IC 62100_62101 Hetman": "EP08-006;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B10ou_01",
|
||||||
|
"IC 62102_62103 Bolko": "EP07-391;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;111a_PKPIC_B9nopuvz_02",
|
||||||
|
"IC 62104_62105 Morcinek": "EP07-1043;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01",
|
||||||
|
"IC 63104_63105 Kossak": "EU07-015;111a_PKPIC_B10ou_01;158a_PKPIC_A9mnopuz_01;113a_PKPIC_WRbd_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01",
|
||||||
|
"IC 65102_65103 Mamry": "EP07-1031;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B9nopuvz_02;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01",
|
||||||
|
"IC 65104_65105 Jeziorak": "EP07-1043;159a_PKPIC_B9mnopuz_01;154a_PKPIC_B10mnouz_01;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;111a_PKPIC_B9nopuvz_02;158a_PKPIC_A9mnopuz_01",
|
||||||
|
"IC 66100 Mehoffer": "EP07-1025;111a_PKPIC_B10ou_01;159a_PKPIC_B9mnopuz_01",
|
||||||
|
"IC 66150 Śnieżka": "SM42-506;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01",
|
||||||
|
"IC 68100_68101 Bosman": "EP07-1002;158a_PKPIC_A9mnopuz_01;158a_PKPIC_A9mnopuz_01;158a_PKPIC_A9mnopuz_01;113a_PKPIC_WRbd_01;158a_PKPIC_A9mnopuz_01;158a_PKPIC_A9mnopuz_01;158a_PKPIC_A9mnopuz_01;158a_PKPIC_A9mnopuz_01;158a_PKPIC_A9mnopuz_01",
|
||||||
|
"IC 68102_68103 Szkuner": "EP07-1056;111a_PKPIC_B10ou_01;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;113a_PKPIC_WRbd_01;158a_PKPIC_A9mnopuz_01;111a_PKPIC_B9nopuvz_02",
|
||||||
|
"IC 68104_68105 Swarożyc": "EP07-1025;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B9nopuvz_02",
|
||||||
|
"IC 71100_71101 Zamenhof": "EP08-006v2;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B10ou_01",
|
||||||
|
"IC 71102_71103 Zielonogórzanin": "EP08-006v2;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B9nopuvz_02",
|
||||||
|
"IC 71104_71105 Warta": "EP08-006v2;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B10ou_01",
|
||||||
|
"IC 72100_72101 Lubuszanin": "EP08-006v2;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01",
|
||||||
|
"IC 75100_75101 Ukiel": "EP07-1043;159a_PKPIC_B9mnopuz_01;154a_PKPIC_B10mnouz_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01",
|
||||||
|
"IC 75102_75103 Drwęca": "EP07-1025;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;159a_PKPIC_B9mnopuz_01;154a_PKPIC_B10mnouz_01;111a_PKPIC_B10ou_01",
|
||||||
|
"IC 75104_75105 Bachus": "EP07-335;111a_PKPIC_B10ou_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01",
|
||||||
|
"IC 75106_75107 Lednica": "EP07-1031;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01",
|
||||||
|
"IC 75108_75109 Stoczniowiec": "EP07-1025;111a_PKPIC_B9nopuvz_02;154a_PKPIC_B10mnouz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01",
|
||||||
|
"IC 78101 Gałczyński": "SM42-506;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01",
|
||||||
|
"IC 78103 Lubuszanin": "SM42-506;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01",
|
||||||
|
"IC 78103 Mewa": "SM42-506;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01",
|
||||||
|
"IC 80150 Rowokół": "EP07-1038;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;113a_PKPIC_WRbd_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B9nopuvz_02",
|
||||||
|
"IC 81100_81101 Gałczyński": "EP08-006v2;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01",
|
||||||
|
"IC 81102_81103 Podlasiak": "EP08-006v2;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01",
|
||||||
|
"IC 81104_81105 Rybak": "EP07-480;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01;154a_PKPIC_B10mnouz_01;159a_PKPIC_B9mnopuz_01",
|
||||||
|
"IC 81106_81107 Mewa": "EP08-006v2;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;158a_PKPIC_A9mnopuz_01;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01",
|
||||||
|
"IC 81112_81113 Noteć": "EP08-006v2;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;158a_PKPIC_A9mnopuz_01;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01",
|
||||||
|
"IC 81152_81153 Parsęta": "EP08-006v2;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01",
|
||||||
|
"IC 83100_83101 Górski": "EP08-006v2;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01",
|
||||||
|
"IC 83102_83103 Osterwa": "EP07-444;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;113a_PKPIC_WRbd_01;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B9nopuvz_02",
|
||||||
|
"IC 83104_83105 Mehoffer": "EP08-006v2;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B9nopuvz_02;158a_PKPIC_A9mnopuz_01;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01",
|
||||||
|
"IC 83192_83193 Podhalanin": "EP07-1056;110a_PKPIC_Bcdu_01;110a_PKPIC_Bcdu_02;158a_PKPIC_A9mnopuz_01;158a_PKPIC_A9mnopuz_01;158a_PKPIC_A9mnopuz_01;158a_PKPIC_A9mnopuz_01;158a_PKPIC_A9mnopuz_01",
|
||||||
|
"IC 84150_84151 Gwarek": "EP07-1043;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B10ou_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01",
|
||||||
|
"IC 85100_85101 Albatros": "EP07-1038;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B10ou_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01",
|
||||||
|
"IC 85102_85103 Żuławy": "EP07-335;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;154a_PKPIC_B10mnouz_01",
|
||||||
|
"IC 85104_85105 Bryza": "EP07-480;159a_PKPIC_B9mnopuz_01;154a_PKPIC_B10mnouz_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B9nopuvz_02",
|
||||||
|
"IC 85106_85107 Gryf": "EP07-444;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01",
|
||||||
|
"IC 85108_85109 Stańczyk": "EU07-077;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01",
|
||||||
|
"IC 86100_86101 Bosman": "EP07-544;158a_PKPIC_A9mnopuz_01;158a_PKPIC_A9mnopuz_01;158a_PKPIC_A9mnopuz_01;113a_PKPIC_WRbd_01;158a_PKPIC_A9mnopuz_01;158a_PKPIC_A9mnopuz_01;158a_PKPIC_A9mnopuz_01",
|
||||||
|
"IC 86102_86103 Szkuner": "EP07-174;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;113a_PKPIC_WRbd_01;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01",
|
||||||
|
"IC 86104_86105 Swarożyc": "EP07-1056;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01",
|
||||||
|
"IC 87100 Gałczyński": "SM42-506;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01",
|
||||||
|
"IC 87102 Lubuszanin": "SM42-506;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01",
|
||||||
|
"IC 87172_87173 Przemyślanin": "EP07-1054;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B9nopuvz_02",
|
||||||
|
"IC 88151 Rowokół": "EP07-444;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01;113a_PKPIC_WRbd_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01",
|
||||||
|
"IC 91100_91101 Lodzianin": "EP08-006v2;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;158a_PKPIC_A9mnopuz_01;111a_PKPIC_B10ou_01;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01",
|
||||||
|
"TLK 10100_10101 Żubr": "EP07-1009;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;112a_PKPIC_A9ou_01;111a_PKPIC_B10ou_01",
|
||||||
|
"TLK 10110_10111 Żubr": "EP07-355;111a_PKPIC_B10ou_01;112a_PKPIC_A9ou_01;111a_PKPIC_B10ou_01",
|
||||||
|
"TLK 12110_12111 Nida": "EP07-444;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;112a_PKPIC_A9ou_01;111a_PKPIC_B10ou_01",
|
||||||
|
"TLK 301_43004_43005 Galicja (PKPIC) R 301 Galicja (ČD) ": "EP07-480;112a_PKPIC_A9ou_01;Bau84_CD_B249_01;Bau84_CD_B10nou_01;Bau84_CD_B10nou_01;Bau84_CD_B10nou_01",
|
||||||
|
"TLK 310_24000_24001 Roztocze (PKPIC) R 310 Roztocze (ČD)": "EP07-480;Bau84_CD_B249_01;Bau84_CD_B249_01;Bau84_CD_B10nou_01;Bau84_CD_B10nou_01;Bau84_CD_B10nou_01;Bau84_CD_B10nou_01",
|
||||||
|
"TLK 31100_31101 Malinowski": "EP07-1025;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B9nopuvz_02;112a_PKPIC_A9ou_01;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01",
|
||||||
|
"TLK 31102_31103 Hańcza": "EP07-480;612a_PKPIC_BD4osuv_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;112a_PKPIC_A9ou_01;111a_PKPIC_B9nopuvz_02;612a_PKPIC_BD4osuv_01;111a_PKPIC_B10ou_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B10ou_01",
|
||||||
|
"TLK 31106_31107 San": "EP07-444;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01",
|
||||||
|
"TLK 31110_31111 Wit Stwosz": "EP08-006;111a_PKPIC_B10ou_01;112a_PKPIC_A9ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;158a_PKPIC_A9mnopuz_01;111a_PKPIC_B10ou_01",
|
||||||
|
"TLK 31150_31151 Karłowicz": "EP07-391;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;112a_PKPIC_A9ou_01;111a_PKPIC_B10ou_01",
|
||||||
|
"TLK 311_42000_42001 Roztocze (PKPIC) R 311 Roztocze (ČD) ": "EP07-480;Bau84_CD_B10nou_01;Bau84_CD_B10nou_01;Bau84_CD_B10nou_01;Bau84_CD_B10nou_01;Bau84_CD_B249_01;Bau84_CD_B249_01",
|
||||||
|
"TLK 33100_33101 Malinowski": "EU07-324;111a_PKPIC_B10ou_01;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B9nopuvz_02",
|
||||||
|
"TLK 33104_33105 Łukasiewicz": "EP07-544;112a_PKPIC_A9ou_01;Bau84_CD_B249_01;Bau84_CD_B249_01;Bau84_CD_B249_01",
|
||||||
|
"TLK 35100_35101 Małopolska": "EP08-006v2;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B9nopuvz_02;112a_PKPIC_A9ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B10ou_01",
|
||||||
|
"TLK 35106_35107 Artus": "EP07-444;111a_PKPIC_B10ou_01;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;112a_PKPIC_A9ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01\n",
|
||||||
|
"TLK 35106_35107 ArtusV2": "EP08-006v2;111a_PKPIC_B10ou_01;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;112a_PKPIC_A9ou_01;111a_PKPIC_B10ou_01\n",
|
||||||
|
"TLK 35170_35171 Karpaty": "EP08-006v2;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B9nopuvz_02;112a_PKPIC_A9ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01",
|
||||||
|
"TLK 35190_35191 Korsarz": "SM42-506;111a_PKPIC_B10ou_01;112a_PKPIC_A9ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;110a_PKPIC_Bcdu_02",
|
||||||
|
"TLK 36102_36103 Sudety": "SM42-506;111a_PKPIC_B10ou_01;112a_PKPIC_A9ou_01;111a_PKPIC_B9nopuvz_02",
|
||||||
|
"TLK 36170_36171 Pogórze": "EP07-1025;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;110a_PKPIC_Bcdu_02\n",
|
||||||
|
"TLK 37100_37101 Pułaski": "EU07-334;Bau84_CD_B249_01;Bau84_CD_B10nou_01;Bau84_CD_B249_01",
|
||||||
|
"TLK 37102_37103 Wybicki": "EP07-444;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;112a_PKPIC_A9ou_01;111a_PKPIC_B10ou_01",
|
||||||
|
"TLK 37170_37171 Pogórze": "EP07-544;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;110a_PKPIC_Bcdu_02;111a_PKPIC_B10ou_01;111a_PKPIC_B9nopuvz_02",
|
||||||
|
"TLK 38106_38107 Zefir": "EU07-334;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;112a_PKPIC_A9ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;112a_PKPIC_A9ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01",
|
||||||
|
"TLK 38154_38155 Lubomirski": "EP08-006;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B10ou_01;112a_PKPIC_A9ou_01;112a_PKPIC_A9ou_01;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;112a_PKPIC_A9ou_01;112a_PKPIC_A9ou_01",
|
||||||
|
"TLK 38170_38171 Ustronie": "EP07-444;110a_PKPIC_Bcdu_01;110a_PKPIC_Bcdu_02;111a_PKPIC_B9nopuvz_02;112a_PKPIC_A9ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01",
|
||||||
|
"TLK 38190_38191 Bursztyn": "EP07-1031;110a_PKPIC_Bcdu_02;112a_PKPIC_A9ou_01;Bau84_CD_B10nou_01;Bau84_CD_B10nou_01;Bau84_CD_B10nou_01;Bau84_CD_B249_01;110a_PKPIC_Bcdu_02",
|
||||||
|
"TLK 400_84070_84071 Wolin (PKPIC) R 400 Wolin (ČD)": "EU07-334;110a_PKPIC_Bcdu_02;Bau84_CD_B10nou_01;Bau84_CD_B10nou_01;Bau84_CD_B10nou_01;612a_PKPIC_BD4osuv_01;Bau84_CD_B249_01;Bau84_CD_B10nou_01;Bau84_CD_B10nou_01",
|
||||||
|
"TLK 40160_40161 Pirat": "EP07-544;612a_PKPIC_BD4osuv_01;Bau84_CD_B10nou_01;Bau84_CD_B10nou_01;Bau84_CD_B10nou_01;110a_PKPIC_Bcdu_02",
|
||||||
|
"TLK 401_48070_48071 Wolin (PKPIC) R 401 Wolin (ČD) ": "EP07-544;Bau84_CD_B249_01;Bau84_CD_B10nou_01;Bau84_CD_B10nou_01;Bau84_CD_B10nou_01;Bau84_CD_B10nou_01;Bau84_CD_B10nou_01;612a_PKPIC_BD4osuv_01;110a_PKPIC_Bcdu_02",
|
||||||
|
"TLK 402_54070_54071 Wydmy (PKPIC) R 402 Wydmy (ČD)": "EP07-1009;Bau84_CD_B10nou_01;Bau84_CD_B10nou_01;Bau84_CD_B10nou_01;Bau84_CD_B249_01;612a_PKPIC_BD4osuv_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;110a_PKPIC_Bcdu_02",
|
||||||
|
"TLK 403_45070_45071 Wydmy (PKPIC) R 403 Wydmy (ČD) ": "EP07-1009;110a_PKPIC_Bcdu_02;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;612a_PKPIC_BD4osuv_01;Bau84_CD_B249_01;Bau84_CD_B10nou_01;Bau84_CD_B10nou_01;Bau84_CD_B10nou_01",
|
||||||
|
"TLK 405_48072_48073 Pirat (PKPIC) R 405 Pirat (ČD) ": "EP07-391;Bau84_CD_B10nou_01;Bau84_CD_B10nou_01;Bau84_CD_B249_01;612a_PKPIC_BD4osuv_01;Bau84_CD_B10nou_01;Bau84_CD_B10nou_01;Bau84_CD_B249_01;Bau84_CD_B10nou_01;110a_PKPIC_Bcdu_02",
|
||||||
|
"TLK 41100_41101 Chemik": "EP07-355;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01\n",
|
||||||
|
"TLK 41102_41103 Wysocki": "EP07-444;111a_PKPIC_B9nopuvz_01;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B9nopuvz_02;112a_PKPIC_A9ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B9nopuvz_01",
|
||||||
|
"TLK 41104_41105 Kmicic": "EP07-1056;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B10ou_01",
|
||||||
|
"TLK 44160_44161 Pirat": "EU07-334;Bau84_CD_B10nou_01;Bau84_CD_B10nou_01;Bau84_CD_B10nou_01;612a_PKPIC_BD4osuv_01;110a_PKPIC_Bcdu_02",
|
||||||
|
"TLK 45100_45101 Doker ": "EP07-1056;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01",
|
||||||
|
"TLK 45150_45151 Halny ": "EP07-1054;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;112a_PKPIC_A9ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01",
|
||||||
|
"TLK 45170_45171 Rozewie ": "EP07-391;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;112a_PKPIC_A9ou_01;111a_PKPIC_B10ou_01;110a_PKPIC_Bcdu_02;110a_PKPIC_Bcdu_02",
|
||||||
|
"TLK 45190_45191 Rozewie ": "EP07-544;111a_PKPIC_B10ou_01;112a_PKPIC_A9ou_01;111a_PKPIC_B10ou_01;110a_PKPIC_Bcdu_02",
|
||||||
|
"TLK 47100_47101 Spodek ": "EP07-1038;112a_PKPIC_A9ou_01;112a_PKPIC_A9ou_01;111a_PKPIC_B10ou_01",
|
||||||
|
"TLK 50150_50151 Gardno": "SM42-506;110a_PKPIC_Bcdu_02;Bau84_CD_B249_01;Bau84_CD_B10nou_01;Bau84_CD_B10nou_01;Bau84_CD_B10nou_01",
|
||||||
|
"TLK 50160_50161 Korsarz": "EP07-1038;110a_PKPIC_Bcdu_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01",
|
||||||
|
"TLK 50170_50171 Mierzeja": "SM42-506;110a_PKPIC_Bcdu_02;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;112a_PKPIC_A9ou_01;111a_PKPIC_B10ou_01",
|
||||||
|
"TLK 51104_51105 Biebrza": "EP07-1025;111a_PKPIC_Bnouz_01;111a_PKPIC_B9nopuvz_02;112a_PKPIC_A9ou_01;111a_PKPIC_B9nopuvz_02;111a_PKPIC_Bnouz_01",
|
||||||
|
"TLK 51112_51113 Kociewie": "EP07-1002;112a_PKPIC_A9ou_01;112a_PKPIC_A9ou_01",
|
||||||
|
"TLK 51150_51151 Delfin": "EP07-335;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;112a_PKPIC_A9ou_01;112a_PKPIC_A9ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01\n",
|
||||||
|
"TLK 51152_51153 Wybrzeże": "EP07-444;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;112a_PKPIC_A9ou_01;112a_PKPIC_A9ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01",
|
||||||
|
"TLK 53100_53101 Małopolska": "EU07-545;EU07-015;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;112a_PKPIC_A9ou_01;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B10ou_01;Bau84_PKPIC_B10nou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01",
|
||||||
|
"TLK 53104_53105 Lubomirski": "EP08-006v2;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B10ou_01;112a_PKPIC_A9ou_01;112a_PKPIC_A9ou_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01",
|
||||||
|
"TLK 53106_53107 Artus V2": "EP07-335;111a_PKPIC_B10ou_01;112a_PKPIC_A9ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B10ou_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B10ou_01",
|
||||||
|
"TLK 53170_53171 Karpaty": "EP08-006v2;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B9nopuvz_02;112a_PKPIC_A9ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01",
|
||||||
|
"TLK 53190_53191 Korsarz": "EP07-1054;110a_PKPIC_Bcdu_02;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;112a_PKPIC_A9ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;110a_PKPIC_Bcdu_02",
|
||||||
|
"TLK 54100_54101 Doker": "EP07-1038;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01",
|
||||||
|
"TLK 54150_54151 Halny": "EP07-174;111a_PKPIC_B10ou_01;112a_PKPIC_A9ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01",
|
||||||
|
"TLK 54170_54171 Rozewie": "EP07-1025;110a_PKPIC_Bcdu_02;110a_PKPIC_Bcdu_02;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;112a_PKPIC_A9ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B9nopuvz_02",
|
||||||
|
"TLK 55154 Wydmy": "EU07-545;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;110a_PKPIC_Bcdu_02",
|
||||||
|
"TLK 55160_55161 Korsarz": "EU07-545;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;110a_PKPIC_Bcdu_02",
|
||||||
|
"TLK 55162_55163 Rozewie": "SM42-506;Bau84_CD_B10nou_01;Bau84_CD_B10nou_01;Bau84_CD_B10nou_01;Bau84_CD_B10nou_01;612a_PKPIC_BD4osuv_01",
|
||||||
|
"TLK 55170_55171 Mierzeja": "SM42-506;111a_PKPIC_B10ou_01;112a_PKPIC_A9ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;110a_PKPIC_Bcdu_02",
|
||||||
|
"TLK 56100_56101 Kormoran": "EP07-355;EP07-1038;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;112a_PKPIC_A9ou_01;111a_PKPIC_B10ou_01",
|
||||||
|
"TLK 58104_58105 Bryza": "EP07-1038;111a_PKPIC_B10ou_01;112a_PKPIC_A9ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B9nopuvz_02",
|
||||||
|
"TLK 58110_58111 Bory Tucholskie": "SM42-506;111a_PKPIC_B10ou_01;112a_PKPIC_A9ou_01;112a_PKPIC_A9ou_01",
|
||||||
|
"TLK 64160_64161 Wolin": "EU07-015;Bau84_CD_B10nou_01;Bau84_CD_B10nou_01;Bau84_CD_B249_01;612a_PKPIC_BD4osuv_01;110a_PKPIC_Bcdu_02",
|
||||||
|
"TLK 65100_65101 Kormoran": "EP07-444;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;112a_PKPIC_A9ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B9nopuvz_02",
|
||||||
|
"TLK 66166_66167 Rozewie": "EP07-391;612a_PKPIC_BD4osuv_01;Bau84_CD_B10nou_01;Bau84_CD_B10nou_01;Bau84_CD_B10nou_01;Bau84_CD_B249_01;110a_PKPIC_Bcdu_02",
|
||||||
|
"TLK 71106_71107 Konopnicka": "EP08-006v2;111a_PKPIC_B10ou_01;112a_PKPIC_A9ou_01;111a_PKPIC_B9nopuvz_02;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01",
|
||||||
|
"TLK 73100_73101 Pułaski": "EP07-1054;Bau84_CD_B249_01;Bau84_CD_B249_01;Bau84_CD_B249_01",
|
||||||
|
"TLK 73102_73103 Wybicki V2": "EP07-1056;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;112a_PKPIC_A9ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01",
|
||||||
|
"TLK 73102_73103 Wybicki": "EP07-1025;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;112a_PKPIC_A9ou_01;111a_PKPIC_B10ou_01;Bau84_PKPIC_B10nou_01",
|
||||||
|
"TLK 73170_73171 Pogórze": "EP07-444;110a_PKPIC_Bcdu_02;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B10ou_01;111a_PKPIC_B9nopuvz_02",
|
||||||
|
"TLK 74100_74101 Spodek": "EP07-544;Bau84_CD_B249_01;Bau84_CD_B249_01;Bau84_CD_B249_01",
|
||||||
|
"TLK 76106_76107 Konopnicka": "SM42-506;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01",
|
||||||
|
"TLK 81114_81115 Pobrzeże": "EP08-006;112a_PKPIC_A9ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;112a_PKPIC_A9ou_01;112a_PKPIC_A9ou_01;112a_PKPIC_A9ou_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B9nopuvz_02;Bau84_PKPIC_B10nou_01;111a_PKPIC_B10ou_01",
|
||||||
|
"TLK 81150_81151 Słupia": "EU07-015;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;112a_PKPIC_A9ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01",
|
||||||
|
"TLK 81170_81171 Uznam": "EP07-480;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;159a_PKPIC_B9mnopuz_01;112a_PKPIC_A9ou_01;111a_PKPIC_B10ou_01;110a_PKPIC_Bcdu_02;110a_PKPIC_Bcdu_02",
|
||||||
|
"TLK 82100_82101 Staszic": "EP08-006v2;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;112a_PKPIC_A9ou_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01",
|
||||||
|
"TLK 82102_82103 Wyczółkowski": "EP07-391;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;159a_PKPIC_B9mnopuz_01;112a_PKPIC_A9ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01",
|
||||||
|
"TLK 82104_82105 Zamoyski": "EP07-480;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;112a_PKPIC_A9ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01",
|
||||||
|
"TLK 82170_82171 Mierzeja": "EP07-1009;110a_PKPIC_Bcdu_02;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;112a_PKPIC_A9ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;112a_PKPIC_A9ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;110a_PKPIC_Bcdu_02",
|
||||||
|
"TLK 83100_83101 Górski": "EP08-006v2;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;158a_PKPIC_A9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01;159a_PKPIC_B9mnopuz_01",
|
||||||
|
"TLK 83106_83107 Zefir": "EP07-1054;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;112a_PKPIC_A9ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;112a_PKPIC_A9ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01",
|
||||||
|
"TLK 83154_83155 Lubomirski": "EP08-006;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B10ou_01;112a_PKPIC_A9ou_01;112a_PKPIC_A9ou_01;159a_PKPIC_B9mnopuz_01;111a_PKPIC_B10ou_01;Bau84_PKPIC_B10nou_01;Bau84_PKPIC_B10nou_01",
|
||||||
|
"TLK 83170_83171 Ustronie V2": "EP07-391;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;112a_PKPIC_A9ou_01;159a_PKPIC_B9mnopuz_01;110a_PKPIC_Bcdu_02;110a_PKPIC_Bcdu_02;110a_PKPIC_Bcdu_02",
|
||||||
|
"TLK 83190_83191 Bursztyn": "EP07-1031;110a_PKPIC_Bcdu_02;Bau84_CD_B10nou_01;Bau84_CD_B10nou_01;Bau84_CD_B10nou_01;Bau84_CD_B10nou_01;112a_PKPIC_A9ou_01;110a_PKPIC_Bcdu_02",
|
||||||
|
"TLK 85104_85105 Bryza": "EP07-1038;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;112a_PKPIC_A9ou_01;111a_PKPIC_B10ou_01",
|
||||||
|
"TLK 86116_86117 Jamno": "EP07-1025;112a_PKPIC_A9ou_01;112a_PKPIC_A9ou_01;112a_PKPIC_A9ou_01;112a_PKPIC_A9ou_01",
|
||||||
|
"TLK 88160_88161 Ustronie": "EU07-015;110a_PKPIC_Bcdu_02;111a_PKPIC_B10ou_01;112a_PKPIC_A9ou_01;111a_PKPIC_B10ou_01",
|
||||||
|
"TLK 91130_91131 Zosia": "EP07-544;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;112a_PKPIC_A9ou_01;112a_PKPIC_A9ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01",
|
||||||
|
"TLK 91132_91133 Korczak": "EP07-1002;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B9nopuvz_02;159a_PKPIC_B9mnopuz_01;112a_PKPIC_A9ou_01;112a_PKPIC_A9ou_01;112a_PKPIC_A9ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01;111a_PKPIC_B10ou_01",
|
||||||
|
"iR 11121 Żubr": "EN57-1953",
|
||||||
|
"iR 11130 Żubr": "EN57-1953",
|
||||||
|
"iR 12121 Bystrzyca": "EN71-028",
|
||||||
|
"iR 12125 Cisy": "EN71-028",
|
||||||
|
"iR 13126 Jan Matejko": "EP07-370;111a_PKP_Bdnu_02;111a_PKP_Bdnu_02;111a_PKP_Bdnu_02;111a_PKP_Bdnu_02;111a_PKP_Bdnu_02;111a_PKP_Bdnu_02",
|
||||||
|
"iR 31130 Semafor": "EN57-1953",
|
||||||
|
"iR 36120 Galicja V2": "EN57-1914",
|
||||||
|
"iR 36120 Galicja": "EN71-010",
|
||||||
|
"iR 36122 Łużyce": "EN57-1747",
|
||||||
|
"iR 40110 Torunianin": "EN71-028",
|
||||||
|
"iR 43127_6 Giewont": "EN57-1542",
|
||||||
|
"iR 43421 Orlik": "EN57-1542",
|
||||||
|
"iR 46121 Spodek": "EN57-1542",
|
||||||
|
"iR 46123 Ostrów Tumski": "EN57-1542",
|
||||||
|
"iR 46125 Oleńka": "EN57-1542",
|
||||||
|
"iR 46140 Szyndzielnia": "EN57-1542",
|
||||||
|
"iR 46142 Szyndzielnia": "EN57-1542",
|
||||||
|
"iR 48102 Pirania": "EP07-338;111a_PRREG_B10_01;112a_PKP_A9nou_01;111a_PRREG_B10_01;112a_PKP_A9nou_01",
|
||||||
|
"iR 51110 Flisak": "EP07-338;111a_PKPIC_B9ouv_01;111a_PKP_Bdnu_02;111a_PKP_Bdnu_02;111a_PRREG_B10_01",
|
||||||
|
"iR 51204 Szczeliniec": "EP07-135;111a_PRREG_B10_01;111a_PRREG_B10_01",
|
||||||
|
"iR 56110 Sokoliki": "EP07-424;111a_PRREG_B10_01;111a_PRREG_Bbnopuvz_01;111a_PRREG_A9ou_01;111a_PRREG_Bbnopuvz_01;111a_PRREG_A9ou_01",
|
||||||
|
"iR 57110 Mamry": "EN57-1542",
|
||||||
|
"iR 57112 Drwęca": "EN57-1542",
|
||||||
|
"iR 57112 Niegocin": "SM42-329;Gor89_PKP_B16mnopux_04;Gor89_PKP_B16mnopux_04;Gor89_PKP_B16mnopux_04",
|
||||||
|
"iR 57120 Lednica": "EN57-1542",
|
||||||
|
"iR 61122 Prosna": "EN57-1542",
|
||||||
|
"iR 61126 Piast": "EP07-338;111a_PKPIC_B9ouv_01;120a_PRREG_B51p_02;120a_PRREG_B51p_02;111a_PRREG_B10_01",
|
||||||
|
"iR 61128 Rudawy": "EP07-242;111a_PRREG_A9ou_01;111a_PRREG_A9ou_01;111a_PKPIC_B9ouv_01;111a_PRREG_B10_01;111a_PRREG_B10_01;111a_PKP_Bnouz_01",
|
||||||
|
"iR 62120 Łysica": "EN57-1747",
|
||||||
|
"iR 63120 Łużyce": "EN57-1542",
|
||||||
|
"iR 64102 Szyndzielnia": "EN57-1542",
|
||||||
|
"iR 64122 Oleńka": "EN57-1542",
|
||||||
|
"iR 64124 Lompa": "EN57-1953",
|
||||||
|
"iR 64126 Spodek": "EN57-1542",
|
||||||
|
"iR 65114 Mamry": "EP07-1008;111a_PRREG_B10_01;112a_PKP_A9nou_01;111a_PKPIC_B9ouv_01;111a_PRREG_B10_01;111a_PRREG_B10_01",
|
||||||
|
"iR 70111 Rusałka": "EN57-1542",
|
||||||
|
"iR 71110 Wielkopolska": "EN57-1747",
|
||||||
|
"iR 71120 Ner": "EU07-475;111a_PKPIC_B9ouv_01;111a_PRREG_B10_01;111a_PRREG_B10_01;Gor89_PKP_B16mnopux_05",
|
||||||
|
"iR 72112 Koziołek": "EN57-1542",
|
||||||
|
"iR 75110 Drwęca": "EN57-1542",
|
||||||
|
"iR 75112 Niegocin": "EU07-222;Gor89_PKP_B16mnopux_05;Gor89_PKP_B16mnopux_05;Gor89_PKP_B16mnopux_05",
|
||||||
|
"iR 75116 Lednica": "EN57-1542",
|
||||||
|
"iR 77111 Rusałka": "EN57-1542",
|
||||||
|
"iR 78111 Delfin": "EN57-1542",
|
||||||
|
"iR 78113 Merkury": "EU07-368;Gor89_PKP_B16mnopux_05;Gor89_PKP_B16mnopux_05;Gor89_PKP_B16mnopux_05",
|
||||||
|
"iR 78115 Wały Chrobrego": "EN57-1542",
|
||||||
|
"iR 78117 Wilda": "EU07-475;Gor89_PKP_B16mnopux_05;Gor89_PKP_B16mnopux_05",
|
||||||
|
"iR 78510 Wolin": "EN57-1747",
|
||||||
|
"iR 81122 Portowiec": "EU07-222;111a_PKPIC_B9ouv_01;111a_Grafitti_1;111a_PRREG_B10_01;111a_PRREG_B10_01;111a_PRREG_A9ouy_01;111a_PKP_Brdu_01;111a_PKP_Brdu_01",
|
||||||
|
"iR 81522 Bosman": "EU07-222;111a_PKPIC_B9ouv_01;111a_PKPIC_B9ouv_01;111a_PRREG_B10_01;111a_PKP_Bdnu_02",
|
||||||
|
"iR 84522 Pirania": "EU07-222;120a_PRREG_B51p_02;111a_PRREG_B10_01;111a_PRREG_A9ou_01;111a_PKPIC_B9ouv_01",
|
||||||
|
"iR 87110 Merkury": "EU07-368;111a_PKPIC_B9ouv_01;111a_PRREG_B10_01;111a_PKPIC_B9ouv_01;111a_PKPIC_B9ouv_01",
|
||||||
|
"iR 87112 Wały Chrobrego": "EN57-1542",
|
||||||
|
"iR 87114 Delfin": "EN57-1542",
|
||||||
|
"iR 87116 Wilda": "SM42-302;Gor89_PKP_B16mnopux_01;Gor89_PKP_B16mnopux_01",
|
||||||
|
"iR 87512 Wolin": "2EN57-1542+1747",
|
||||||
|
"RE 14120 Chemik": "EN57-1747",
|
||||||
|
"RE 18122 Mewa": "EU07-222;111a_PRREG_Bbnopuvz_01;111a_PRREG_Bbnopuvz_02;111a_PRREG_Bbnopuvz_01;111a_PRREG_Bbnopuvz_01;111a_PRREG_Bbnopuvz_01;112a_PKP_Adu_01;112a_PKP_Adu_01;112a_PKP_Adu_01",
|
||||||
|
"RE 26122 Bolko": "EU07-222;111a_PRREG_A9ou_02;112a_PKP_A9nou_01;111a_PRREG_A9ou_01;111a_PRREG_B10_01",
|
||||||
|
"RE 27120 Warta": "EU06-18;111a_PRREG_A9ou_02;111a_PRREG_Bbnopuvz_01;111a_PRREG_Bbnopuvz_01;111a_PRREG_Bbnopuvz_01;111a_PRREG_Bbnopuvz_02",
|
||||||
|
"RE 41120 Chemik": "EU07-222;111a_PKPIC_B9ouv_01;111a_PRREG_Bbnopuvz_01;111a_PRREG_Bbnopuvz_01;111a_PRREG_Bbnopuvz_01;111a_PRREG_Bbnopuvz_01;111a_PRREG_Bbnopuvz_02",
|
||||||
|
"RE 62122 Bolko": "EP07-1055;Gor89_PKP_B16mnopux_04;111a_PRREG_A9ou_02;111a_PRREG_Bbnopuvz_01;111a_PRREG_Bbnopuvz_01;111a_PRREG_Bbnopuvz_01",
|
||||||
|
"RE 72110 Warta": "EU07-222;111a_PRREG_A9ou_02;111a_PRREG_Bbnopuvz_01;111a_PRREG_Bbnopuvz_01;111a_PRREG_Bbnopuvz_01;111a_PRREG_Bbnopuvz_02",
|
||||||
|
"RE 81120 Mewa": "EU07-222;111a_PRREG_Bbnopuvz_01;111a_PRREG_Bbnopuvz_02;111a_PRREG_Bbnopuvz_01;111a_PRREG_Bbnopuvz_01;111a_PRREG_Bbnopuvz_01"
|
||||||
|
}
|
||||||
@@ -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"
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
const http = axios.create({
|
||||||
|
baseURL:
|
||||||
|
import.meta.env.VITE_API_DEV === '1' && import.meta.env.DEV
|
||||||
|
? 'http://localhost:3001'
|
||||||
|
: 'https://stacjownik.spythere.eu',
|
||||||
|
});
|
||||||
|
|
||||||
|
export default http;
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import localePL from './locales/pl.json';
|
||||||
|
import localeEN from './locales/en.json';
|
||||||
|
import { createI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
type LocaleMessageSchema = typeof localePL;
|
||||||
|
type LocaleKey = 'en' | 'pl';
|
||||||
|
|
||||||
|
const locales: { [key in LocaleKey]: LocaleMessageSchema } = {
|
||||||
|
en: localeEN,
|
||||||
|
pl: localePL,
|
||||||
|
};
|
||||||
|
|
||||||
|
const locale =
|
||||||
|
window.localStorage.getItem('locale') || (/^pl\b/.test(navigator.language) ? 'pl' : 'en');
|
||||||
|
|
||||||
|
const i18n = createI18n<[LocaleMessageSchema], 'en' | 'pl'>({
|
||||||
|
locale,
|
||||||
|
fallbackLocale: 'pl',
|
||||||
|
legacy: false,
|
||||||
|
globalInjection: true,
|
||||||
|
messages: locales,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default i18n;
|
||||||