Compare commits
102 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 794eb97b3b | |||
| 4fce109332 | |||
| 9e4c69e87d | |||
| 7e789b0d9f | |||
| 19ed3d6a1c | |||
| bfd14a8471 | |||
| 039c52a8b7 | |||
| b373cbcba1 | |||
| 7858088db0 | |||
| 1c93684218 | |||
| 1a969731cd | |||
| d408f0a706 | |||
| c97806c920 | |||
| b1e3346b59 | |||
| 1f8365340b | |||
| 4d06b85fc1 | |||
| d5d0551c17 | |||
| 35a883d608 | |||
| 2efa4a4f9a | |||
| 2c5eb27484 | |||
| 92586fb880 | |||
| e0e0504366 | |||
| 3c9cdac832 | |||
| 4162f5e137 | |||
| b2b5716cc6 | |||
| 26b0556bfa | |||
| d59152fccd | |||
| 864967a77a | |||
| 37c2650841 | |||
| 5b9b86248f | |||
| 43b0dc9fa0 | |||
| 972c73281d | |||
| 8538072a60 | |||
| e298a17ab7 | |||
| 519665697b | |||
| c5221e337b | |||
| dcea5f7102 | |||
| 2f5e61352a | |||
| 1d9df380fd | |||
| 153d6ff0db | |||
| a9a486a789 | |||
| 85772df972 | |||
| 0464f4eae9 | |||
| 339b3edd1e | |||
| 069b27beaa | |||
| 7b18f84db1 | |||
| 42d0d1ddb9 | |||
| b1a51bbd74 | |||
| f28f1e31c2 | |||
| 0930dab77c | |||
| e81521ed09 | |||
| b69c44c55a | |||
| fab3cef1f3 | |||
| 99e55bb8b9 | |||
| d050cf0b89 | |||
| 1b8bf5cc45 | |||
| a180215dd0 | |||
| 5649e7c417 | |||
| 8b8d69132d | |||
| af579f2cba | |||
| d0052f8dd3 | |||
| 28ba4045a0 | |||
| d76c9a8521 | |||
| 10d5beae3c | |||
| d84ad635ee | |||
| ae0beadb93 | |||
| 758bb9f997 | |||
| 85c4fba8ba | |||
| af2af08fbc | |||
| 8aa2233d41 | |||
| f663c96842 | |||
| b663f7408d | |||
| 05f3f8f8b2 | |||
| bb285f9ba6 | |||
| 43d74ddaea | |||
| 53be6c8cf2 | |||
| 39236ece3e | |||
| c40e699b40 | |||
| 76c6169be9 | |||
| 76c1e97ddd | |||
| 71c9a47ea5 | |||
| a066da006c | |||
| 1783ae1fce | |||
| 7921bd46e9 | |||
| 189720c206 | |||
| e5e0751024 | |||
| 45ac3248ac | |||
| 06d33a30ed | |||
| e7fd51142d | |||
| bed61c38f6 | |||
| a1d81986c6 | |||
| e4ee67918a | |||
| f85bd13765 | |||
| f2015a2af5 | |||
| f7a456e791 | |||
| 262042c5b1 | |||
| e8574efa42 | |||
| 2119399c45 | |||
| df25fb0b36 | |||
| 94235df38d | |||
| 3155487a29 | |||
| dc2062d906 |
@@ -1,2 +1,5 @@
|
||||
VITE_APP_API_URL=https://stacjownik-api-b9mrc.ondigitalocean.app/api
|
||||
VITE_APP_SWDR_URL=https://api.td2.info.pl:9640
|
||||
VITE_APP_API_URL=https://stacjownik.spythere.eu/api
|
||||
VITE_APP_SWDR_URL=https://api.td2.info.pl
|
||||
|
||||
VITE_APP_ORDER_VERSION=2
|
||||
#VITE_UPDATE_TEST='test'
|
||||
@@ -0,0 +1,17 @@
|
||||
/* eslint-env node */
|
||||
|
||||
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,5 +1,5 @@
|
||||
{
|
||||
"projects": {
|
||||
"default": "genera-tor"
|
||||
"default": "generator-td2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,16 +5,16 @@ name: Deploy to Firebase Hosting on merge
|
||||
'on':
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- main-old
|
||||
jobs:
|
||||
build_and_deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- run: npm ci && npm run build
|
||||
- run: yarn && yarn build
|
||||
- uses: FirebaseExtended/action-hosting-deploy@v0
|
||||
with:
|
||||
repoToken: '${{ secrets.GITHUB_TOKEN }}'
|
||||
firebaseServiceAccount: '${{ secrets.FIREBASE_SERVICE_ACCOUNT_GENERA_TOR }}'
|
||||
firebaseServiceAccount: '${{ secrets.FIREBASE_SERVICE_ACCOUNT_GENERATOR_TD2 }}'
|
||||
channelId: live
|
||||
projectId: genera-tor
|
||||
projectId: generator-td2
|
||||
|
||||
@@ -9,9 +9,9 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- run: npm ci && npm run build
|
||||
- run: yarn && yarn build
|
||||
- uses: FirebaseExtended/action-hosting-deploy@v0
|
||||
with:
|
||||
repoToken: '${{ secrets.GITHUB_TOKEN }}'
|
||||
firebaseServiceAccount: '${{ secrets.FIREBASE_SERVICE_ACCOUNT_GENERA_TOR }}'
|
||||
projectId: genera-tor
|
||||
firebaseServiceAccount: '${{ secrets.FIREBASE_SERVICE_ACCOUNT_GENERATOR_TD2 }}'
|
||||
projectId: generator-td2
|
||||
|
||||
@@ -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: '9936031'
|
||||
footer_title: 'Changelog - GeneraTOR'
|
||||
footer_timestamp: true
|
||||
@@ -0,0 +1,23 @@
|
||||
name: Build & Deploy to VPS
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
env:
|
||||
PROJECT_NAME: generator-td2
|
||||
|
||||
jobs:
|
||||
build_and_deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Build the app
|
||||
run: yarn && yarn build
|
||||
- name: Setup SSH key for connection with the server
|
||||
run: |
|
||||
mkdir -p ~/.ssh
|
||||
echo "${{ secrets.VPS_SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa && chmod 600 ~/.ssh/id_rsa
|
||||
- name: Send new files
|
||||
run: rsync -avP -e "ssh -o StrictHostKeyChecking=no -i ~/.ssh/id_rsa -p 2022" ./dist/ ${{ secrets.VPS_USER }}@${{ secrets.VPS_HOST }}:/var/www/$PROJECT_NAME --delete
|
||||
@@ -2,6 +2,7 @@
|
||||
node_modules
|
||||
/dev-dist
|
||||
.log
|
||||
/dist
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/prettierrc",
|
||||
"tabWidth": 2,
|
||||
"singleQuote": true,
|
||||
"printWidth": 100,
|
||||
"trailingComma": "none"
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
'@vue/cli-plugin-babel/preset'
|
||||
]
|
||||
}
|
||||
|
Before Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 5.8 KiB |
@@ -1,4 +0,0 @@
|
||||
<svg width="144" height="144" viewBox="0 0 144 144" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M76.3333 17.9839C76.3267 15.5906 74.3812 13.6578 71.988 13.6667C69.5948 13.6756 67.6601 15.6229 67.6667 18.0161L76.3333 17.9839ZM72.2408 105.15L97.1394 61.7232L47.1027 61.9097L72.2408 105.15ZM67.6667 18.0161L67.7997 66.1659L76.4663 66.1336L76.3333 17.9839L67.6667 18.0161Z" fill="white"/>
|
||||
<path d="M14 83.2536V127H131V83.2536" stroke="white" stroke-width="11.2667" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 530 B |
@@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<browserconfig>
|
||||
<msapplication>
|
||||
<tile>
|
||||
<square150x150logo src="/mstile-150x150.png"/>
|
||||
<TileColor>#00aba9</TileColor>
|
||||
</tile>
|
||||
</msapplication>
|
||||
</browserconfig>
|
||||
|
Before Width: | Height: | Size: 650 B |
|
Before Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 15 KiB |
@@ -1,36 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="pl">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
|
||||
|
||||
<!-- Favicon -->
|
||||
<link rel="apple-touch-icon" sizes="57x57" href="/apple-icon-57x57.png" />
|
||||
<link rel="apple-touch-icon" sizes="60x60" href="/apple-icon-60x60.png" />
|
||||
<link rel="apple-touch-icon" sizes="72x72" href="/apple-icon-72x72.png" />
|
||||
<link rel="apple-touch-icon" sizes="76x76" href="/apple-icon-76x76.png" />
|
||||
<link rel="apple-touch-icon" sizes="114x114" href="/apple-icon-114x114.png" />
|
||||
<link rel="apple-touch-icon" sizes="120x120" href="/apple-icon-120x120.png" />
|
||||
<link rel="apple-touch-icon" sizes="144x144" href="/apple-icon-144x144.png" />
|
||||
<link rel="apple-touch-icon" sizes="152x152" href="/apple-icon-152x152.png" />
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-icon-180x180.png" />
|
||||
<link rel="icon" type="image/png" sizes="192x192" href="/android-icon-192x192.png" />
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
|
||||
<link rel="icon" type="image/png" sizes="96x96" href="/favicon-96x96.png" />
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
<meta name="msapplication-TileColor" content="#ffffff" />
|
||||
<meta name="msapplication-TileImage" content="/ms-icon-144x144.png" />
|
||||
<meta name="theme-color" content="#ffffff" />
|
||||
|
||||
<title>GeneraTOR</title>
|
||||
<meta name="description" content="Generator rozkazów pisemnych online" />
|
||||
<script type="module" crossorigin src="/assets/index.d3173749.js"></script>
|
||||
<link rel="stylesheet" href="/assets/index.705ed11d.css">
|
||||
<link rel="manifest" href="/manifest.webmanifest"><script id="vite-plugin-pwa:register-sw" src="/registerSW.js"></script></head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,23 +0,0 @@
|
||||
{
|
||||
"name": "GeneraTOR",
|
||||
"short_name": "GeneraTOR",
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"background_color": "#313638",
|
||||
"theme_color": "#ff6060",
|
||||
"description": "Generator rozkazów pisemnych",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/android-chrome-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/android-chrome-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "any maskable"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
{"name":"genera-tor","short_name":"genera-tor","start_url":"/","display":"standalone","background_color":"#ffffff","lang":"en","scope":"/"}
|
||||
|
Before Width: | Height: | Size: 4.5 KiB |
@@ -1 +0,0 @@
|
||||
if('serviceWorker' in navigator) {window.addEventListener('load', () => {navigator.serviceWorker.register('/sw.js', { scope: '/' })})}
|
||||
@@ -1,42 +0,0 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
width="512.000000pt" height="512.000000pt" viewBox="0 0 512.000000 512.000000"
|
||||
preserveAspectRatio="xMidYMid meet">
|
||||
<metadata>
|
||||
Created by potrace 1.14, written by Peter Selinger 2001-2017
|
||||
</metadata>
|
||||
<g transform="translate(0.000000,512.000000) scale(0.100000,-0.100000)"
|
||||
fill="#000000" stroke="none">
|
||||
<path d="M2310 5109 c-502 -55 -974 -249 -1355 -556 -129 -104 -340 -321 -433
|
||||
-445 -265 -354 -434 -758 -498 -1193 -22 -147 -29 -428 -15 -575 42 -433 180
|
||||
-833 405 -1174 414 -629 1047 -1032 1791 -1142 147 -22 428 -29 575 -15 433
|
||||
42 833 180 1174 405 629 414 1032 1047 1142 1791 22 147 29 428 15 575 -51
|
||||
524 -238 988 -558 1385 -104 129 -321 340 -445 433 -354 265 -755 432 -1193
|
||||
498 -121 18 -487 26 -605 13z m-520 -1749 l0 -120 -325 0 -325 0 0 -470 0
|
||||
-470 213 2 212 3 3 158 3 157 -96 0 -95 0 0 110 0 110 218 -2 217 -3 0 -280 0
|
||||
-280 -30 -60 c-36 -73 -95 -116 -180 -133 -88 -18 -457 -16 -529 3 -117 31
|
||||
-176 97 -196 221 -13 80 -13 852 0 933 20 122 76 188 184 215 88 23 91 23 424
|
||||
25 l302 1 0 -120z m459 60 c16 -23 152 -228 303 -457 l273 -416 3 457 2 456
|
||||
110 0 110 0 0 -660 c0 -653 0 -659 20 -670 18 -10 170 -162 256 -255 23 -26
|
||||
179 -190 434 -458 41 -43 79 -90 84 -103 22 -65 -25 -134 -91 -134 -30 0 -46
|
||||
11 -112 75 l-77 75 -995 0 -995 0 -69 -70 c-62 -62 -74 -70 -108 -70 -53 0
|
||||
-87 35 -87 91 0 38 6 48 78 123 42 44 116 121 163 171 47 49 132 140 190 200
|
||||
58 61 151 158 207 217 l102 108 0 680 0 680 85 0 85 0 29 -40z m1817 25 c62
|
||||
-18 111 -54 146 -107 45 -67 53 -108 53 -298 0 -211 -12 -257 -84 -329 -25
|
||||
-26 -56 -54 -69 -62 l-23 -14 31 -42 c20 -27 60 -123 111 -267 43 -123 79
|
||||
-227 79 -230 0 -3 -57 -6 -128 -6 l-127 0 -79 238 c-43 130 -85 247 -92 260
|
||||
-14 21 -20 22 -154 22 l-140 0 0 -260 0 -260 -130 0 -130 0 0 678 c0 373 3
|
||||
682 7 685 3 4 158 7 343 7 267 0 347 -3 386 -15z"/>
|
||||
<path d="M2270 2537 l0 -447 -30 0 c-28 0 -30 -2 -30 -39 0 -30 -8 -49 -36
|
||||
-83 l-37 -43 428 -3 c236 -1 430 0 433 3 3 2 -10 21 -28 41 -28 33 -32 43 -27
|
||||
80 l4 43 -41 3 -40 3 -284 430 c-155 237 -289 437 -297 445 -13 12 -15 -42
|
||||
-15 -433z"/>
|
||||
<path d="M2024 1808 c-33 -35 -65 -70 -71 -78 -10 -13 60 -15 618 -15 346 0
|
||||
629 1 629 3 0 2 -32 37 -72 78 l-71 74 -486 0 -487 0 -60 -62z"/>
|
||||
<path d="M1770 1540 l-92 -100 893 0 893 0 -44 48 c-25 26 -67 71 -95 100
|
||||
l-49 52 -707 0 -707 0 -92 -100z"/>
|
||||
<path d="M3590 3035 l0 -205 203 2 202 3 3 203 2 202 -205 0 -205 0 0 -205z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.5 KiB |
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"name": "",
|
||||
"short_name": "",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/android-chrome-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/android-chrome-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"theme_color": "#ffffff",
|
||||
"background_color": "#ffffff",
|
||||
"display": "standalone"
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
if(!self.define){let e,i={};const n=(n,r)=>(n=new URL(n+".js",r).href,i[n]||new Promise((i=>{if("document"in self){const e=document.createElement("script");e.src=n,e.onload=i,document.head.appendChild(e)}else e=n,importScripts(n),i()})).then((()=>{let e=i[n];if(!e)throw new Error(`Module ${n} didn’t register its module`);return e})));self.define=(r,s)=>{const o=e||("document"in self?document.currentScript.src:"")||location.href;if(i[o])return;let d={};const c=e=>n(e,o),t={module:{uri:o},exports:d,require:c};i[o]=Promise.all(r.map((e=>t[e]||c(e)))).then((e=>(s(...e),d)))}}define(["./workbox-3625d7b0"],(function(e){"use strict";self.skipWaiting(),e.clientsClaim(),e.precacheAndRoute([{url:"android-chrome-192x192.png",revision:"587a303e0bc5130743216d71e95e2b3f"},{url:"android-chrome-512x512.png",revision:"a2b75707c3b4e4dc7be3c0493a2e11fd"},{url:"apple-touch-icon.png",revision:"a72231c73410e833e7542f9ae34b4c4d"},{url:"assets/icon-save.5a12487e.svg",revision:null},{url:"assets/index.705ed11d.css",revision:null},{url:"assets/index.d3173749.js",revision:null},{url:"favicon-16x16.png",revision:"90a108cf04a9bd9990266d9d8337ed6a"},{url:"favicon-32x32.png",revision:"8411776198fb04093e6862bc7a2f7734"},{url:"index.html",revision:"c54b79fee86bfc30080bbf0b58d85200"},{url:"mstile-150x150.png",revision:"7930e41cef90048a1570dabcecd74d0b"},{url:"registerSW.js",revision:"1872c500de691dce40960bb85481de07"},{url:"safari-pinned-tab.svg",revision:"bfc326d45774cbb917a60157231c7a39"},{url:"manifest.webmanifest",revision:"a0d456bb8bd7fce3475d10d340ee9776"}],{}),e.cleanupOutdatedCaches(),e.registerRoute(new e.NavigationRoute(e.createHandlerBoundToURL("index.html")))}));
|
||||
@@ -1,4 +1,4 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="pl">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
@@ -24,9 +24,71 @@
|
||||
<meta name="msapplication-TileImage" content="/ms-icon-144x144.png" />
|
||||
<meta name="theme-color" content="#ffffff" />
|
||||
|
||||
<!-- Preloads -->
|
||||
<link
|
||||
rel="preload"
|
||||
href="/fonts/libre-franklin-500.woff2"
|
||||
as="font"
|
||||
type="font/woff2"
|
||||
crossorigin
|
||||
/>
|
||||
<link
|
||||
rel="preload"
|
||||
href="/fonts/libre-franklin-700.woff2"
|
||||
as="font"
|
||||
type="font/woff2"
|
||||
crossorigin
|
||||
/>
|
||||
<link
|
||||
rel="preload"
|
||||
href="/fonts/libre-franklin-800.woff2"
|
||||
as="font"
|
||||
type="font/woff2"
|
||||
crossorigin
|
||||
/>
|
||||
<link
|
||||
rel="preload"
|
||||
href="/fonts/libre-franklin-regular.woff2"
|
||||
as="font"
|
||||
type="font/woff2"
|
||||
crossorigin
|
||||
/>
|
||||
|
||||
<link rel="preload" href="/favicon.ico" as="image" />
|
||||
|
||||
<title>GeneraTOR</title>
|
||||
<meta name="description" content="Generator rozkazów pisemnych online" />
|
||||
<meta name="description" content="Generator rozkazów pisemnych dla symulatora Train Driver 2" />
|
||||
|
||||
<!-- Static OpenGraph meta -->
|
||||
<meta property="og:url" content="https://generator-td2.web.app/" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:title" content="GeneraTOR" />
|
||||
<meta
|
||||
property="og:description"
|
||||
content="Generator rozkazów pisemnych dla symulatora Train Driver 2"
|
||||
/>
|
||||
|
||||
<meta
|
||||
property="og:image"
|
||||
content="https://raw.githubusercontent.com/Spythere/api/refs/heads/main/thumbnails/generator-banner.png"
|
||||
/>
|
||||
|
||||
<meta property="og:image:width" content="1200" />
|
||||
<meta property="og:image:height" content="630" />
|
||||
<meta property="og:site_name" content="GeneraTOR" />
|
||||
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta name="twitter:title" content="GeneraTOR" />
|
||||
<meta
|
||||
name="twitter:description"
|
||||
content="Generator rozkazów pisemnych dla symulatora Train Driver 2"
|
||||
/>
|
||||
<meta
|
||||
name="twitter:image"
|
||||
content="https://raw.githubusercontent.com/Spythere/api/refs/heads/main/thumbnails/generator-banner.png"
|
||||
/>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
|
||||
@@ -1,26 +1,38 @@
|
||||
{
|
||||
"name": "genera-tor",
|
||||
"version": "1.1.3dev",
|
||||
"version": "1.7.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite --port 8080",
|
||||
"deploy": "yarn build && firebase deploy --only hosting",
|
||||
"build": "vue-tsc --noEmit && vite build",
|
||||
"preview": "vite preview"
|
||||
"preview": "vite preview",
|
||||
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
|
||||
"format": "prettier --write src/"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.1.3",
|
||||
"pinia": "^2.0.14",
|
||||
"vue": "^3.2.37",
|
||||
"vue-i18n": "9",
|
||||
"vue-router": "^4.0.0-0",
|
||||
"vue-tsc": "^1.0.9"
|
||||
"axios": "^1.6.2",
|
||||
"lucide-vue-next": "^0.562.0",
|
||||
"pinia": "^2.1.7",
|
||||
"showdown": "^2.1.0",
|
||||
"vue": "^3.3.11",
|
||||
"vue-i18n": "11",
|
||||
"vue-router": "^4.2.5",
|
||||
"vue-tsc": "^2.2.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^3.2.0",
|
||||
"sass": "^1.56.0",
|
||||
"typescript": "^4.8.4",
|
||||
"vite": "^3.2.2",
|
||||
"vite-plugin-pwa": "^0.13.2"
|
||||
"@types/node": "^22.13.10",
|
||||
"@types/showdown": "^2.0.6",
|
||||
"@vitejs/plugin-vue": "^4.5.2",
|
||||
"@vue/eslint-config-prettier": "^8.0.0",
|
||||
"@vue/eslint-config-typescript": "^12.0.0",
|
||||
"eslint": "^8.49.0",
|
||||
"eslint-plugin-vue": "^9.17.0",
|
||||
"prettier": "^3.0.3",
|
||||
"sass": "^1.69.5",
|
||||
"typescript": "^5.3.3",
|
||||
"vite": "^5.0.7",
|
||||
"vite-plugin-pwa": "^0.17.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,89 +1,169 @@
|
||||
<template>
|
||||
<div id="app_wrapper">
|
||||
<router-view />
|
||||
|
||||
<transition name="slide-anim">
|
||||
<div v-if="needRefresh" class="update-prompt" @click="updateServiceWorker(true)">Nowa wersja GeneraTORa dostępna! <u>Kliknij, aby odświeżyć aplikację!</u></div>
|
||||
<UpdateCard />
|
||||
</transition>
|
||||
|
||||
<footer>
|
||||
© <a href="https://td2.info.pl/profile/?u=20777">Spythere</a> {{ new Date().getUTCFullYear() }} | v.{{
|
||||
appVersion
|
||||
}}
|
||||
</footer>
|
||||
<transition name="slide-anim">
|
||||
<UpdatePrompt />
|
||||
</transition>
|
||||
|
||||
<transition name="slide-anim">
|
||||
<MigrationInfo v-if="store.isMigrationInfoOpen" />
|
||||
</transition>
|
||||
|
||||
<div class="app-body">
|
||||
<Navbar />
|
||||
|
||||
<main>
|
||||
<RouterView />
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { useRegisterSW } from 'virtual:pwa-register/vue';
|
||||
import { defineComponent } from 'vue';
|
||||
import UpdateCard from './components/Global/UpdateCard.vue';
|
||||
import orderStorageMixin from './mixins/orderStorageMixin';
|
||||
import { useStore } from './store/store';
|
||||
import packageInfo from '../package.json';
|
||||
import axios from 'axios';
|
||||
import StorageManager from './managers/storageManager';
|
||||
import Navbar from './components/App/Navbar.vue';
|
||||
import UpdatePrompt from './components/Global/UpdatePrompt.vue';
|
||||
import MigrationInfo from './components/Global/MigrationInfo.vue';
|
||||
|
||||
const STORAGE_VERSION_KEY = 'app_version';
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
const { offlineReady, needRefresh, updateServiceWorker } = useRegisterSW({
|
||||
immediate: true,
|
||||
});
|
||||
components: { UpdateCard, UpdatePrompt, Navbar, MigrationInfo },
|
||||
|
||||
mixins: [orderStorageMixin],
|
||||
|
||||
return {
|
||||
offlineReady,
|
||||
needRefresh,
|
||||
updateServiceWorker,
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
appVersion: packageInfo.version,
|
||||
needRefreshTest: false,
|
||||
};
|
||||
return { appVersion: packageInfo.version, store: useStore() };
|
||||
},
|
||||
|
||||
created() {
|
||||
document.title = `GeneraTOR ${this.appVersion}`;
|
||||
|
||||
setTimeout(() => {
|
||||
this.needRefreshTest = true;
|
||||
}, 500);
|
||||
this.init();
|
||||
},
|
||||
|
||||
methods: {
|
||||
init() {
|
||||
this.loadLang();
|
||||
this.setupDarkMode();
|
||||
this.loadSettings();
|
||||
this.handleMigrationInfo();
|
||||
this.checkAppVersion();
|
||||
this.handleQueries();
|
||||
},
|
||||
|
||||
loadSettings() {
|
||||
document.title = `GeneraTOR ${this.appVersion}`;
|
||||
},
|
||||
|
||||
setupDarkMode() {
|
||||
if (this.getOrderSetting('dark-mode') === null) {
|
||||
const prefersDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
|
||||
this.saveOrderSetting('dark-mode', prefersDarkMode);
|
||||
}
|
||||
|
||||
this.store.orderDarkMode = this.getOrderSetting('dark-mode') === 'true';
|
||||
document.documentElement.setAttribute(
|
||||
'data-theme',
|
||||
this.store.orderDarkMode ? 'dark' : 'light'
|
||||
);
|
||||
},
|
||||
|
||||
handleQueries() {
|
||||
const query = new URLSearchParams(window.location.search);
|
||||
|
||||
const id = query.get('sceneryId');
|
||||
|
||||
if (id != null) {
|
||||
this.store.panelMode = 'OrderTrainPickerPanel';
|
||||
}
|
||||
},
|
||||
|
||||
async checkAppVersion() {
|
||||
const storageVersion = StorageManager.getStringValue(STORAGE_VERSION_KEY);
|
||||
|
||||
try {
|
||||
const releaseData = await (
|
||||
await axios.get('https://api.github.com/repos/Spythere/genera-tor/releases/latest')
|
||||
).data;
|
||||
|
||||
if (!releaseData) return;
|
||||
|
||||
this.store.appUpdateData.version = this.appVersion;
|
||||
this.store.appUpdateData.changelog = releaseData.body;
|
||||
this.store.appUpdateData.releaseURL = releaseData.html_url;
|
||||
|
||||
this.store.updateCardOpen =
|
||||
(storageVersion != '' && storageVersion != this.appVersion) ||
|
||||
import.meta.env.VITE_UPDATE_TEST === 'test';
|
||||
} catch (error) {
|
||||
console.error(`Wystąpił błąd podczas pobierania danych z API GitHuba: ${error}`);
|
||||
}
|
||||
|
||||
StorageManager.setStringValue(STORAGE_VERSION_KEY, this.appVersion);
|
||||
},
|
||||
|
||||
changeLang(lang: string) {
|
||||
this.$i18n.locale = lang;
|
||||
this.store.currentAppLocale = lang;
|
||||
|
||||
StorageManager.setStringValue('lang', lang);
|
||||
},
|
||||
|
||||
loadLang() {
|
||||
const storageLang = StorageManager.getStringValue('lang');
|
||||
|
||||
if (storageLang) {
|
||||
this.changeLang(storageLang);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!window.navigator.language) return;
|
||||
|
||||
const naviLanguage = window.navigator.language.toString();
|
||||
|
||||
if (!naviLanguage.startsWith('pl')) {
|
||||
this.changeLang('en');
|
||||
}
|
||||
},
|
||||
|
||||
handleMigrationInfo() {
|
||||
// Show only on old domain
|
||||
if (location.hostname !== 'generator-td2.web.app' && location.hostname != 'localhost') return;
|
||||
|
||||
const showInfo = localStorage.getItem('showMigrationInfo');
|
||||
|
||||
// Do not show if already acknowledged
|
||||
if (showInfo === 'false') return;
|
||||
|
||||
setTimeout(() => {
|
||||
this.store.isMigrationInfoOpen = true;
|
||||
}, 2000);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import './styles/global.scss';
|
||||
@import './styles/anims.scss';
|
||||
|
||||
@use 'styles/anims';
|
||||
@use 'styles/colors';
|
||||
|
||||
#app {
|
||||
color: white;
|
||||
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.update-prompt {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
padding: 0.5em;
|
||||
|
||||
font-weight: bold;
|
||||
|
||||
text-align: center;
|
||||
|
||||
width: 100%;
|
||||
background-color: $accentCol;
|
||||
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
footer {
|
||||
text-align: center;
|
||||
padding: 0.5em 0;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 500px) {
|
||||
#app {
|
||||
font-size: calc(1vw + 0.5rem);
|
||||
font-size: calc(1vw + 0.65rem);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
<template>
|
||||
<nav class="app-navbar">
|
||||
<div class="navbar-brand">
|
||||
<img src="/favicon.ico" alt="generator logo" width="30" />
|
||||
<div>
|
||||
<b>
|
||||
Genera<span class="text--accent">TOR</span>
|
||||
<sup class="text--grayed">v{{ version }}</sup>
|
||||
</b>
|
||||
|
||||
<b class="brand-author"> by Spythere</b>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="navbar-actions">
|
||||
<button class="g-button action icon" @click="switchDarkMode">
|
||||
<LucideMoon :size="20" v-if="store.orderDarkMode" />
|
||||
<LucideSun :size="20" v-else />
|
||||
</button>
|
||||
|
||||
<button class="g-button action icon" @click="switchLang">
|
||||
<LucideGlobe :size="20" />
|
||||
<span>{{ store.currentAppLocale == 'pl' ? 'POL' : 'ENG' }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { LucideGlobe, LucideMoon, LucideSun } from 'lucide-vue-next';
|
||||
import { version } from '../../../package.json';
|
||||
import { useStore } from '../../store/store';
|
||||
|
||||
const store = useStore();
|
||||
|
||||
function switchDarkMode() {
|
||||
store.orderDarkMode = !store.orderDarkMode;
|
||||
window.localStorage.setItem('dark-mode', `${store.orderDarkMode}`);
|
||||
document.documentElement.setAttribute('data-theme', store.orderDarkMode ? 'dark' : 'light');
|
||||
}
|
||||
|
||||
function switchLang() {
|
||||
store.changeLang(store.currentAppLocale == 'pl' ? 'en' : 'pl');
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.app-navbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
padding: 0.25em;
|
||||
background-color: #1c1c1c;
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5em;
|
||||
|
||||
sup {
|
||||
font-size: 0.75em;
|
||||
}
|
||||
}
|
||||
|
||||
.brand-author {
|
||||
font-size: 0.8em;
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
.navbar-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5em;
|
||||
|
||||
button {
|
||||
padding: 0.5em;
|
||||
gap: 0.25em;
|
||||
border-radius: 0.5em;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,67 @@
|
||||
<template>
|
||||
<div class="migrate-info">
|
||||
<div class="info-content">
|
||||
<i18n-t keypath="migrate-info.line-1" for="migrate-info" tag="div">
|
||||
<a href="https://generator-td2.spythere.eu/" target="_blank">{{
|
||||
t('migrate-info.link')
|
||||
}}</a>
|
||||
</i18n-t>
|
||||
<button class="g-button action accept-btn" @click="onAcceptButtonClick">
|
||||
{{ t('migrate-info.accept-btn') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useStore } from '../../store/store';
|
||||
|
||||
const store = useStore();
|
||||
const { t } = useI18n();
|
||||
|
||||
function onAcceptButtonClick() {
|
||||
store.isMigrationInfoOpen = false;
|
||||
localStorage.setItem('showMigrationInfo', 'false');
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@use '../../styles/colors';
|
||||
|
||||
.migrate-info {
|
||||
position: fixed;
|
||||
z-index: 100;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
|
||||
padding: 0.25em;
|
||||
text-align: center;
|
||||
|
||||
width: 100%;
|
||||
background-color: colors.$warnCol;
|
||||
color: black;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.info-content {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5em;
|
||||
}
|
||||
|
||||
a {
|
||||
color: black;
|
||||
text-decoration: underline;
|
||||
|
||||
&:hover {
|
||||
color: black;
|
||||
}
|
||||
}
|
||||
|
||||
.accept-btn {
|
||||
color: white;
|
||||
}
|
||||
</style>
|
||||
@@ -11,11 +11,14 @@
|
||||
<li>
|
||||
<b>Przetaczanie (manewr) taboru poza wskaźnik W 5 (granicy przetaczania)</b>
|
||||
<p>
|
||||
Rozkazu <u>nie stosujemy</u> w przypadku wyjazdu taboru na szlak dwutorowy, na którym odbywa się ruch w
|
||||
kierunku zasadniczym, tj. "prawostronnym". Dla wszystkich szlaków jednotorowych lub dwutorowych w sytuacji innej niż wymieniona
|
||||
wykorzystujemy działkę 4. rozkazu pisemnego "S".
|
||||
<br><br>
|
||||
<i>Szczegółowe informacje: instrukcja Ir-1 (R-1) § 12 pkt 4 "Manewry na torach głównych"</i>
|
||||
Rozkazu <u>nie stosujemy</u> w przypadku wyjazdu taboru na szlak dwutorowy, na którym
|
||||
odbywa się ruch w kierunku zasadniczym, tj. "prawostronnym". Dla wszystkich szlaków
|
||||
jednotorowych lub dwutorowych w sytuacji innej niż wymieniona wykorzystujemy działkę 4.
|
||||
rozkazu pisemnego "S". <br /><br />
|
||||
<i
|
||||
>Szczegółowe informacje: instrukcja Ir-1 (R-1) § 12 pkt 4 "Manewry na torach
|
||||
głównych"</i
|
||||
>
|
||||
</p>
|
||||
<button class="g-button action">Wygeneruj treść rozkazu</button>
|
||||
</li>
|
||||
@@ -26,21 +29,17 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { useStore } from '../store/store';
|
||||
import orderHelperData from '../data/orderHelperData.json';
|
||||
import { useStore } from '../../store/store';
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
return {
|
||||
store: useStore(),
|
||||
orderHelperData,
|
||||
};
|
||||
},
|
||||
return { store: useStore() };
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../styles/global.scss";
|
||||
@use '../../styles/colors';
|
||||
|
||||
.content {
|
||||
width: 100%;
|
||||
@@ -63,7 +62,7 @@ ul {
|
||||
|
||||
li b {
|
||||
font-size: 1.1em;
|
||||
color: $accentCol;
|
||||
color: colors.$accentCol;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,143 @@
|
||||
<template>
|
||||
<div class="update-card" v-if="store.updateCardOpen" @toggle-card="toggleCard(false)">
|
||||
<div class="card-background"></div>
|
||||
<div class="card-content">
|
||||
<h1 style="margin-bottom: 0.5em">🚀 {{ $t('update.title') }}</h1>
|
||||
|
||||
<div class="changelog" v-if="htmlChangelog != ''" v-html="htmlChangelog"></div>
|
||||
<div class="no-features" v-else>{{ $t('update.no-data') }}</div>
|
||||
|
||||
<button class="g-button action btn-confirm" ref="confirmButtonEl" @click="toggleCard(false)">
|
||||
{{ $t('update.confirm') }}
|
||||
</button>
|
||||
|
||||
<p class="bottom-info">
|
||||
{{ $t('update.info-1') }}
|
||||
<br />
|
||||
<span v-html="$t('update.info-2')"></span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { Converter } from 'showdown';
|
||||
import { useStore } from '../../store/store';
|
||||
|
||||
const converter = new Converter();
|
||||
const store = useStore();
|
||||
const confirmButtonEl = ref<HTMLButtonElement | null>(null);
|
||||
|
||||
watch(
|
||||
computed(() => store.updateCardOpen),
|
||||
(val) => {
|
||||
if (val) {
|
||||
confirmButtonEl.value?.focus();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const htmlChangelog = computed(() => {
|
||||
if (store.appUpdateData.changelog == '') return '';
|
||||
|
||||
return converter.makeHtml(store.appUpdateData.changelog);
|
||||
});
|
||||
|
||||
function toggleCard(value: boolean) {
|
||||
store.updateCardOpen = value;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
// Converter styles
|
||||
::v-deep(h1) {
|
||||
text-align: center;
|
||||
color: var(--clr-primary);
|
||||
}
|
||||
|
||||
::v-deep(h2) {
|
||||
padding: 0.25em 0;
|
||||
border-bottom: 1px solid #aaa;
|
||||
}
|
||||
|
||||
::v-deep(ul) {
|
||||
list-style: disc;
|
||||
padding: 1em;
|
||||
line-height: 1.5em;
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
.update-card {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 200;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.card-background {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
z-index: 250;
|
||||
|
||||
cursor: pointer;
|
||||
|
||||
background-color: rgba(0, 0, 0, 0.55);
|
||||
}
|
||||
|
||||
.card-content {
|
||||
display: grid;
|
||||
grid-template-rows: auto 1fr auto;
|
||||
gap: 0.5em;
|
||||
|
||||
margin: 1em;
|
||||
|
||||
max-height: 95vh;
|
||||
max-height: 95dvh;
|
||||
|
||||
background-color: #1a1a1a;
|
||||
box-shadow: 0 0 15px 10px #0e0e0e;
|
||||
border-radius: 1em;
|
||||
|
||||
overflow: auto;
|
||||
|
||||
padding: 1em;
|
||||
min-height: 700px;
|
||||
overflow: auto;
|
||||
max-width: 700px;
|
||||
|
||||
z-index: 300;
|
||||
}
|
||||
|
||||
.no-features {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.changelog {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
button.btn-confirm {
|
||||
padding: 0.5em 0.75em;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
p.bottom-info {
|
||||
text-align: center;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<div v-if="needRefresh" class="update-prompt" @click="updateServiceWorker(true)">
|
||||
{{ $t('update.update-available-text') }}
|
||||
<u>{{ $t('update.update-available-underline') }}</u>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useRegisterSW } from 'virtual:pwa-register/vue';
|
||||
|
||||
const { needRefresh, updateServiceWorker } = useRegisterSW({ immediate: true });
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@use '../../styles/colors';
|
||||
|
||||
.update-prompt {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
padding: 0.5em;
|
||||
|
||||
z-index: 200;
|
||||
|
||||
font-weight: bold;
|
||||
|
||||
text-align: center;
|
||||
|
||||
width: 100%;
|
||||
background-color: colors.$accentCol;
|
||||
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
@@ -1,190 +0,0 @@
|
||||
<template>
|
||||
<div class="order">
|
||||
<div class="order_content">
|
||||
<transition name="order-anim" mode="out-in">
|
||||
<keep-alive>
|
||||
<component :is="chosenOrderComponent" :key="chosenOrderComponent.name"></component>
|
||||
</keep-alive>
|
||||
</transition>
|
||||
<OrderFooter />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { useStore } from '../store/store';
|
||||
import OrderNVue from './OrderN.vue';
|
||||
import OrderSVue from './OrderS.vue';
|
||||
import OrderFooter from './OrderFooter.vue';
|
||||
import OrderOVue from './OrderO.vue';
|
||||
|
||||
const orderComponents = {
|
||||
orderN: OrderNVue,
|
||||
orderS: OrderSVue,
|
||||
orderO: OrderOVue,
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
components: { OrderNVue, OrderSVue, OrderFooter },
|
||||
|
||||
setup() {
|
||||
const store = useStore();
|
||||
|
||||
return {
|
||||
store,
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
chosenOrderComponent() {
|
||||
return orderComponents[this.store.chosenOrderType];
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import '../styles/global.scss';
|
||||
|
||||
// Order scrollbar
|
||||
::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: #ccc;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: #aaa;
|
||||
}
|
||||
|
||||
.order {
|
||||
background-color: white;
|
||||
color: black;
|
||||
|
||||
height: 925px;
|
||||
max-height: 95vh;
|
||||
overflow: auto;
|
||||
|
||||
font-size: 15px;
|
||||
|
||||
h2 {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.header {
|
||||
padding: 0.5em;
|
||||
border: 2px solid black;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 550px) {
|
||||
font-size: 3vw;
|
||||
}
|
||||
}
|
||||
|
||||
.order_content {
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
.flex-row {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: flex-end;
|
||||
|
||||
margin-top: 0.5em;
|
||||
|
||||
input {
|
||||
max-width: 10em;
|
||||
}
|
||||
}
|
||||
|
||||
.flex-center {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
input {
|
||||
max-width: 100px;
|
||||
background-color: transparent;
|
||||
outline: none;
|
||||
border: none;
|
||||
|
||||
border-bottom: 2px dotted black;
|
||||
font-size: 0.9em;
|
||||
text-align: center;
|
||||
color: black;
|
||||
|
||||
&:focus-visible {
|
||||
border-bottom: 2px solid $accentCol;
|
||||
}
|
||||
|
||||
&.row-checkbox + input::placeholder {
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
|
||||
input[type='checkbox'],
|
||||
input[type='radio'],
|
||||
textarea,
|
||||
select {
|
||||
&:focus-visible {
|
||||
outline: 2px solid $accentCol;
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
margin-top: 0.5em;
|
||||
margin-right: 0.5em;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
.table-section {
|
||||
table.options-table {
|
||||
width: 100%;
|
||||
|
||||
td:first-child {
|
||||
width: 10%;
|
||||
|
||||
text-align: center;
|
||||
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 0.35em;
|
||||
text-align: justify;
|
||||
vertical-align: top;
|
||||
|
||||
line-height: 1.5em;
|
||||
}
|
||||
}
|
||||
|
||||
table,
|
||||
td {
|
||||
border: 2px solid black;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
}
|
||||
|
||||
.order-anim {
|
||||
&-enter-active,
|
||||
&-leave-active {
|
||||
transition: opacity 150ms ease-in-out;
|
||||
}
|
||||
|
||||
&-enter-from,
|
||||
&-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,147 +0,0 @@
|
||||
<template>
|
||||
<section class="order-list">
|
||||
<h3>Zapisane rozkazy pisemne ({{ localOrderList.length }})</h3>
|
||||
|
||||
<transition-group name="list" tag="ul">
|
||||
<li class="no-orders-warning" v-if="sortedOrderList.length == 0" :key="-1">Brak zapisanych rozkazów!</li>
|
||||
|
||||
<li v-for="order in sortedOrderList" :selected="order.id == store.chosenLocalOrderId" :key="order.id">
|
||||
<b class="text--accent">#{{ order.id.split('-')[1] }} </b>
|
||||
<b>
|
||||
{{ getOrderName(order.orderType) }} nr {{ order.orderBody['header']['orderNo'] }} dla pociągu nr
|
||||
{{ order.orderBody['header']['trainNo'] }}
|
||||
</b>
|
||||
<br />
|
||||
{{ order.createdAt ? 'Dodano: ' : 'Zaktualizowano: ' }}
|
||||
{{ new Date(order.createdAt || order.updatedAt || 0).toLocaleString('pl-PL') }}
|
||||
<br />
|
||||
<button class="g-button action" @click="selectLocalOrder(order)">Wybierz</button>
|
||||
<button class="g-button action" @click="removeOrder(order)">Usuń</button>
|
||||
</li>
|
||||
</transition-group>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import orderStorageMixin from '../mixins/orderStorageMixin';
|
||||
import { useStore } from '../store/store';
|
||||
import { LocalStorageOrder } from '../types/orderTypes';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'OrderList',
|
||||
mixins: [orderStorageMixin],
|
||||
|
||||
data() {
|
||||
return {
|
||||
localOrderList: [] as LocalStorageOrder[],
|
||||
};
|
||||
},
|
||||
|
||||
setup() {
|
||||
return {
|
||||
store: useStore(),
|
||||
localStorage: window.localStorage,
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
getOrderName(orderType: string) {
|
||||
return `Rozkaz "${orderType.split('order')[1]}"`;
|
||||
},
|
||||
|
||||
removeOrder(order: LocalStorageOrder) {
|
||||
if (!order) return;
|
||||
|
||||
this.removeLocalOrder(order);
|
||||
|
||||
this.localOrderList = this.localOrderList.filter((o) => o.id != order.id);
|
||||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
sortedOrderList() {
|
||||
return this.localOrderList.sort((a, b) => (b.createdAt || b.updatedAt!) - (a.createdAt || a.updatedAt!));
|
||||
},
|
||||
},
|
||||
|
||||
activated() {
|
||||
const localStorage = window.localStorage;
|
||||
const orderList = [];
|
||||
|
||||
for (let key in localStorage) {
|
||||
if (!/^order-/g.test(key)) continue;
|
||||
|
||||
const orderObj: LocalStorageOrder = JSON.parse(localStorage[key]);
|
||||
if (!orderObj) continue;
|
||||
|
||||
orderList.push(orderObj);
|
||||
}
|
||||
|
||||
this.localOrderList = orderList;
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../styles/global.scss';
|
||||
|
||||
.list {
|
||||
&-move,
|
||||
&-enter-active,
|
||||
&-leave-active {
|
||||
transition: all 250ms ease;
|
||||
}
|
||||
&-enter-from,
|
||||
&-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateY(30px);
|
||||
}
|
||||
|
||||
&-leave-active {
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
|
||||
.order-list {
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
ul {
|
||||
max-height: 750px;
|
||||
height: 80vh;
|
||||
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin: 0;
|
||||
margin-bottom: 1em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
li {
|
||||
text-align: left;
|
||||
padding: 1em;
|
||||
margin: 0.5em;
|
||||
background-color: #222;
|
||||
|
||||
cursor: pointer;
|
||||
|
||||
button {
|
||||
margin: 1em 1em 0 0;
|
||||
}
|
||||
|
||||
&[selected='true'] {
|
||||
outline: 1px solid $accentCol;
|
||||
}
|
||||
&.no-orders-warning {
|
||||
text-align: center;
|
||||
font-size: 1.2em;
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,225 +0,0 @@
|
||||
<template>
|
||||
<section class="order-message">
|
||||
<h3>Wiadomość do wyświetlenia na czacie symulatora:</h3>
|
||||
|
||||
<div class="message_body" v-html="fullOrderMessage"></div>
|
||||
<p class="message_info">
|
||||
Po wygenerowaniu rozkazu skopiuj jego treść lub zapisz w pamięci przeglądarki za pomocą przycisków poniżej
|
||||
</p>
|
||||
|
||||
<div class="message_actions">
|
||||
<button class="g-button action" @click="saveOrder">Zapisz nowy rozkaz</button>
|
||||
<button class="g-button action" @click="copyMessage">Kopiuj treść rozkazu</button>
|
||||
<button class="g-button action" :data-disabled="!store.chosenLocalOrderId" @click="updateOrder">
|
||||
Zaktualizuj rozkaz
|
||||
<span class="text--accent"
|
||||
>{{ store.chosenLocalOrderId && `#${store.chosenLocalOrderId.split('-')[1]}` }}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<transition name="monit-anim">
|
||||
<div class="action_monit" v-if="actionMonit" v-html="actionMonit"></div>
|
||||
</transition>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { useStore } from '../store/store';
|
||||
|
||||
import saveIcon from '../assets/icon-save.svg';
|
||||
import orderStorageMixin from '../mixins/orderStorageMixin';
|
||||
import orderValidationMixin from '../mixins/orderValidationMixin';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'OrderMessage',
|
||||
|
||||
mixins: [orderStorageMixin, orderValidationMixin],
|
||||
|
||||
data() {
|
||||
return {
|
||||
saveIcon,
|
||||
actionMonit: '',
|
||||
monitTimeout: undefined as number | undefined,
|
||||
};
|
||||
},
|
||||
|
||||
setup() {
|
||||
return {
|
||||
store: useStore(),
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
fullOrderMessage() {
|
||||
return this.store.orderMessage + this.store.footerMessage;
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
showActionMonit(text: string) {
|
||||
if (this.monitTimeout) {
|
||||
this.actionMonit = '';
|
||||
clearTimeout(this.monitTimeout);
|
||||
|
||||
setTimeout(() => {
|
||||
this.actionMonit = text;
|
||||
|
||||
this.monitTimeout = window.setTimeout(() => {
|
||||
this.actionMonit = '';
|
||||
}, 5000);
|
||||
}, 300);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.actionMonit = text;
|
||||
|
||||
this.monitTimeout = window.setTimeout(() => {
|
||||
this.actionMonit = '';
|
||||
}, 5000);
|
||||
},
|
||||
|
||||
copyMessage() {
|
||||
if (!navigator.clipboard)
|
||||
return this.showActionMonit(
|
||||
'Ups! Twoja przeglądarka musi być dosyć przestarzała, ponieważ nie obsługuje zapisu do schowka! :/'
|
||||
);
|
||||
|
||||
const hasAtLeastOneRow = /(\[ \d \])/g.test(this.fullOrderMessage);
|
||||
const hasAllInputsFilled = !/_/g.test(this.store.orderMessage);
|
||||
|
||||
if (!hasAllInputsFilled) return this.showActionMonit(`<span class="text--warn">Wypełnij puste rubryki rozkazu przed jego skopiowaniem!</span>`);
|
||||
if (!hasAtLeastOneRow) return this.showActionMonit(`<span class="text--warn">Dodaj co najmniej jedną działkę rozkazu przed jego skopiowaniem!</span>`);
|
||||
|
||||
const fieldsToCorrect = this.verifyOrderFields();
|
||||
|
||||
if (fieldsToCorrect.length > 0)
|
||||
return this.showActionMonit(
|
||||
`<span class="text--warn">Uzupełnij następujące rubryki na dole rozkazu przed jego skopiowaniem: ${fieldsToCorrect.join(
|
||||
', '
|
||||
)}</span>`
|
||||
);
|
||||
|
||||
navigator.clipboard.writeText(this.fullOrderMessage);
|
||||
|
||||
this.showActionMonit(
|
||||
'<b class="text--accent">Skopiowano!</b> Możesz teraz wkleić treść rozkazu na czacie symulatora!'
|
||||
);
|
||||
},
|
||||
|
||||
saveOrder() {
|
||||
const savedOrderStatus = this.saveLocalOrder();
|
||||
|
||||
switch (savedOrderStatus) {
|
||||
case -1:
|
||||
this.showActionMonit(
|
||||
'<span class="text--warn">Wypełnij numer rozkazu, numer pociągu i datę zanim dodasz rozkaz!</span>'
|
||||
);
|
||||
break;
|
||||
case 0:
|
||||
this.showActionMonit('<span class="text--warn">Ostatni zapisany rozkaz jest identyczny z obecnym!</span>');
|
||||
break;
|
||||
case 1:
|
||||
this.showActionMonit('Zapisano treść <b class="text--accent">rozkazu</b> w pamięci przeglądarki!');
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
updateOrder() {
|
||||
const updatedOrderStatus = this.updateLocalOrder();
|
||||
|
||||
switch (updatedOrderStatus) {
|
||||
case -1:
|
||||
this.showActionMonit('<span class="text--warn">Wystąpił błąd podczas aktualizowania tego rozkazu! :/</span>');
|
||||
break;
|
||||
|
||||
case 0:
|
||||
this.showActionMonit('<span class="text--warn">Nie wybrałeś żadnego zapisanego rozkazu!</span>');
|
||||
break;
|
||||
|
||||
case 1:
|
||||
this.showActionMonit('Zaktualizowano treść <b class="text--accent">rozkazu</b>!');
|
||||
break;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.order-message {
|
||||
padding: 1em;
|
||||
|
||||
h3 {
|
||||
margin: 0;
|
||||
margin-bottom: 1em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
button {
|
||||
margin: 0 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
.message_body {
|
||||
height: 250px;
|
||||
overflow: auto;
|
||||
text-align: justify;
|
||||
|
||||
background-color: #fff;
|
||||
border-radius: 0.5em;
|
||||
color: black;
|
||||
padding: 0.5em;
|
||||
user-select: none;
|
||||
-moz-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
.message_info {
|
||||
text-align: center;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.message_actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-top: 1em;
|
||||
|
||||
button img {
|
||||
height: 2ch;
|
||||
vertical-align: text-bottom;
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
|
||||
button[data-disabled='true'] {
|
||||
user-select: none;
|
||||
color: #aaa;
|
||||
}
|
||||
}
|
||||
|
||||
.action_monit {
|
||||
text-align: center;
|
||||
padding: 1.5em;
|
||||
font-size: 1.15em;
|
||||
}
|
||||
|
||||
.monit-anim {
|
||||
&-enter-active,
|
||||
&-leave-active {
|
||||
transition: all 100ms ease-in-out;
|
||||
}
|
||||
|
||||
&-enter-from,
|
||||
&-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateY(-20px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,251 +0,0 @@
|
||||
<template>
|
||||
<div class="order-train-picker">
|
||||
<div class="options">
|
||||
<select name="dispatcher-select" id="dispatcher-select" v-model="selectedDispatcherName">
|
||||
<option :value="null" disabled>Nick dyżurnego</option>
|
||||
<option v-for="dispatcherName in dispatcherNameList" :value="dispatcherName">
|
||||
{{ dispatcherName }}
|
||||
</option>
|
||||
</select>
|
||||
|
||||
<select
|
||||
name="scenery-select"
|
||||
id="scenery-select"
|
||||
v-model="selectedSceneryName"
|
||||
:disabled="sceneryNameList.length == 0"
|
||||
>
|
||||
<option :value="null" disabled>Sceneria</option>
|
||||
<option :value="sceneryName" v-for="sceneryName in sceneryNameList">
|
||||
{{ sceneryName }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<b v-if="!selectedSceneryName" class="text--accent"> Wybierz dyżurnego oraz scenerię, aby zobaczyć pociągi </b>
|
||||
|
||||
<div v-else>
|
||||
<b class="text--accent">Kliknij na gracza, aby wypełnić obecny rozkaz jego danymi</b>
|
||||
|
||||
<p>Gracze online bez RJ</p>
|
||||
<ul class="train-list">
|
||||
<li v-for="train in sceneryTrains" @click="fillOrder(train.trainNo)">
|
||||
<b>{{ train.trainNo }} | {{ train.driverName }}</b>
|
||||
</li>
|
||||
|
||||
<li class="no-trains" v-if="sceneryTrains.length == 0 && selectedSceneryName">Brak graczy</li>
|
||||
</ul>
|
||||
|
||||
<p>Aktywne rozkłady jazdy</p>
|
||||
<ul class="train-list">
|
||||
<li v-for="train in sceneryScheduledTrains" @click="fillOrder(train.trainNo)">
|
||||
<b>{{ train.trainNo }} | {{ train.driverName }}</b>
|
||||
</li>
|
||||
|
||||
<li class="no-trains" v-if="sceneryScheduledTrains.length == 0">Brak aktywnych rozkładów</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import axios from 'axios';
|
||||
import { ApiSWDR, ApiStacjownik } from '../types/apiTypes';
|
||||
import { useStore } from '../store/store';
|
||||
import { currentFormattedDate } from '../utils/dateUtils';
|
||||
import { ISceneryOnline, ISceneryData } from '../types/dataTypes';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'order-train-picker',
|
||||
|
||||
data() {
|
||||
return {
|
||||
sceneriesData: [] as ISceneryData[],
|
||||
sceneriesOnline: [] as ISceneryOnline[],
|
||||
trainsOnline: [] as ApiStacjownik.IActiveTrain[],
|
||||
|
||||
selectedSceneryName: null as string | null,
|
||||
selectedDispatcherName: null as string | null,
|
||||
|
||||
refreshInterval: -1,
|
||||
store: useStore(),
|
||||
};
|
||||
},
|
||||
|
||||
created() {
|
||||
this.fetchSceneriesData();
|
||||
},
|
||||
|
||||
activated() {
|
||||
this.fetchOnlineData();
|
||||
|
||||
this.refreshInterval = window.setInterval(() => {
|
||||
this.fetchOnlineData();
|
||||
}, 35 * 1000);
|
||||
},
|
||||
|
||||
deactivated() {
|
||||
window.clearInterval(this.refreshInterval);
|
||||
},
|
||||
|
||||
watch: {
|
||||
selectedDispatcherName() {
|
||||
this.selectedSceneryName = this.sceneryNameList.length == 0 ? null : this.sceneryNameList[0];
|
||||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
selectedSceneryHash() {
|
||||
return this.sceneriesOnline.find((s) => this.selectedSceneryName == s.stationName)?.stationHash;
|
||||
},
|
||||
|
||||
sceneriesOnlinePL1() {
|
||||
return this.sceneriesOnline.filter((s) => s.region == 'eu' && s.isOnline);
|
||||
},
|
||||
|
||||
dispatcherNameList() {
|
||||
return [...new Set(this.sceneriesOnlinePL1.map((s) => s.dispatcherName))].sort((a, b) =>
|
||||
a.toLocaleLowerCase() < b.toLocaleLowerCase() ? -1 : 1
|
||||
);
|
||||
},
|
||||
|
||||
sceneryNameList() {
|
||||
return this.sceneriesOnlinePL1
|
||||
.filter((s) => s.dispatcherName == this.selectedDispatcherName)
|
||||
.map((s) => s.stationName)
|
||||
.sort((a, b) => (a < b ? -1 : 1));
|
||||
},
|
||||
|
||||
sceneryTrains() {
|
||||
return this.trainsOnline.filter(
|
||||
(t) => t.online && t.currentStationName == this.selectedSceneryName && this.selectedSceneryName && !t.timetable
|
||||
);
|
||||
},
|
||||
|
||||
sceneryScheduledTrains() {
|
||||
if (!this.selectedSceneryHash) return [];
|
||||
const hash = this.selectedSceneryHash;
|
||||
|
||||
return this.trainsOnline
|
||||
.filter((t) => t.timetable?.sceneries.includes(hash))
|
||||
.sort((t1, t2) => t1.trainNo - t2.trainNo);
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
async fetchSceneriesData() {
|
||||
const data: ISceneryData[] = await (await axios.get(`${import.meta.env['VITE_APP_API_URL']}/getSceneries`)).data;
|
||||
|
||||
if (!data) return;
|
||||
|
||||
this.sceneriesData = data;
|
||||
},
|
||||
|
||||
async fetchOnlineData() {
|
||||
this.fetchSceneriesOnline();
|
||||
this.fetchTrainsOnline();
|
||||
},
|
||||
|
||||
async fetchSceneriesOnline() {
|
||||
const data: ApiSWDR.IStationsOnline = await (
|
||||
await axios.get(`${import.meta.env['VITE_APP_SWDR_URL']}/?method=getStationsOnline`)
|
||||
).data;
|
||||
|
||||
if (!data.success) return;
|
||||
|
||||
this.sceneriesOnline = data.message;
|
||||
},
|
||||
|
||||
async fetchTrainsOnline() {
|
||||
const data: ApiStacjownik.IActiveTrain[] = await (
|
||||
await axios.get(`${import.meta.env['VITE_APP_API_URL']}/getActiveTrainList`)
|
||||
).data;
|
||||
|
||||
if (!data) return;
|
||||
|
||||
this.trainsOnline = data;
|
||||
},
|
||||
|
||||
fillOrder(trainNo: number) {
|
||||
if (!this.selectedDispatcherName || !this.selectedSceneryName) return;
|
||||
|
||||
const chosenOrder = this.store[this.store.chosenOrderType];
|
||||
chosenOrder.header.trainNo = trainNo.toString();
|
||||
chosenOrder.header.date = currentFormattedDate();
|
||||
|
||||
this.store.orderFooter.dispatcherName = this.selectedDispatcherName;
|
||||
this.store.orderFooter.stationName = this.selectedSceneryName;
|
||||
|
||||
this.store.orderMode = 'OrderMessage';
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../styles/global.scss';
|
||||
|
||||
.order-train-picker {
|
||||
padding: 1em;
|
||||
height: 90vh;
|
||||
|
||||
overflow-y: auto;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
.options {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
gap: 1em;
|
||||
|
||||
select {
|
||||
border: 2px solid white;
|
||||
color: white;
|
||||
font-size: 1em;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0.15em;
|
||||
}
|
||||
|
||||
option {
|
||||
color: black;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
margin-top: 1em;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
|
||||
p {
|
||||
margin: 1em 0;
|
||||
font-weight: bold;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
}
|
||||
|
||||
ul.train-list {
|
||||
li {
|
||||
background-color: #111;
|
||||
padding: 0.5em;
|
||||
margin-top: 0.5em;
|
||||
|
||||
cursor: pointer;
|
||||
|
||||
&.no-trains {
|
||||
font-weight: bold;
|
||||
background-color: #222;
|
||||
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,247 @@
|
||||
<template>
|
||||
<div class="order-container">
|
||||
<OrderSideBar />
|
||||
|
||||
<div class="order" :class="{ dark: store.orderDarkMode }">
|
||||
<div class="order_content">
|
||||
<transition name="order-anim" mode="out-in">
|
||||
<keep-alive>
|
||||
<component :is="chosenOrderComponent" :key="chosenOrderComponent.name"></component>
|
||||
</keep-alive>
|
||||
</transition>
|
||||
<OrderFooter />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { useStore } from '../../store/store';
|
||||
import OrderN from './OrderN.vue';
|
||||
import OrderS from './OrderS.vue';
|
||||
import OrderO from './OrderO.vue';
|
||||
import OrderFooter from './OrderFooter.vue';
|
||||
import OrderSideBar from './OrderSideBar.vue';
|
||||
|
||||
const orderComponents = { orderN: OrderN, orderS: OrderS, orderO: OrderO };
|
||||
|
||||
export default defineComponent({
|
||||
components: { OrderN, OrderO, OrderS, OrderFooter, OrderSideBar },
|
||||
|
||||
setup() {
|
||||
const store = useStore();
|
||||
|
||||
return { store };
|
||||
},
|
||||
|
||||
computed: {
|
||||
chosenOrderComponent() {
|
||||
return orderComponents[this.store.chosenOrderType];
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@use '../../styles/colors';
|
||||
|
||||
$darkModeTextCol: #eee;
|
||||
|
||||
.order-container {
|
||||
display: flex;
|
||||
align-items: start;
|
||||
max-width: 800px;
|
||||
|
||||
@media screen and (max-width: 1150px) {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
.order {
|
||||
background-color: white;
|
||||
color: black;
|
||||
|
||||
max-height: calc(100vh - 5em);
|
||||
overflow: auto;
|
||||
|
||||
&.dark {
|
||||
background-color: colors.$bgColDarker;
|
||||
color: $darkModeTextCol;
|
||||
}
|
||||
|
||||
font-size: 15px;
|
||||
|
||||
h2 {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
textarea:focus-visible {
|
||||
outline: 2px solid colors.$accentCol;
|
||||
}
|
||||
|
||||
input[type='checkbox']:focus-visible,
|
||||
input[type='radio']:focus-visible {
|
||||
outline: 2px solid colors.$accentCol;
|
||||
}
|
||||
|
||||
input[type='checkbox'],
|
||||
input[type='radio'],
|
||||
select {
|
||||
margin-top: 0.5em;
|
||||
margin-right: 0.5em;
|
||||
font-size: 0.8em;
|
||||
color: black;
|
||||
}
|
||||
|
||||
textarea.others {
|
||||
width: 100%;
|
||||
min-height: 200px;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 550px) {
|
||||
font-size: 3vw;
|
||||
}
|
||||
}
|
||||
|
||||
.order_header {
|
||||
padding: 0.5em;
|
||||
border: 2px solid black;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.order_content {
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
.flex-row {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: flex-end;
|
||||
|
||||
margin-top: 0.5em;
|
||||
|
||||
input {
|
||||
max-width: 10em;
|
||||
}
|
||||
}
|
||||
|
||||
.flex-center {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.horizontal-bar {
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
|
||||
background-color: black;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
|
||||
.order input {
|
||||
max-width: 100px;
|
||||
background-color: transparent;
|
||||
outline: none;
|
||||
border: none;
|
||||
font-size: 1em;
|
||||
|
||||
text-align: center;
|
||||
border-bottom: 2px dotted black;
|
||||
|
||||
&:focus-visible {
|
||||
border-bottom: 2px solid colors.$accentCol;
|
||||
}
|
||||
|
||||
&.row-checkbox + input::placeholder {
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
|
||||
/* Dark mode */
|
||||
.order.dark {
|
||||
input {
|
||||
border-color: $darkModeTextCol !important;
|
||||
color: $darkModeTextCol !important;
|
||||
|
||||
&:focus-visible {
|
||||
border-bottom: 2px solid colors.$accentCol !important;
|
||||
}
|
||||
|
||||
&::placeholder {
|
||||
color: #ccc !important;
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
color: $darkModeTextCol !important;
|
||||
border-color: $darkModeTextCol;
|
||||
|
||||
&:focus-visible {
|
||||
border-color: colors.$accentCol;
|
||||
}
|
||||
}
|
||||
|
||||
option,
|
||||
textarea {
|
||||
color: $darkModeTextCol !important;
|
||||
border-color: $darkModeTextCol !important;
|
||||
background-color: colors.$bgColDarker !important;
|
||||
}
|
||||
|
||||
.horizontal-bar {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.order_header,
|
||||
.order_other,
|
||||
table,
|
||||
tr,
|
||||
td {
|
||||
border-color: $darkModeTextCol !important;
|
||||
}
|
||||
}
|
||||
|
||||
.order_table-container {
|
||||
table {
|
||||
width: 100%;
|
||||
|
||||
td:first-child {
|
||||
width: 10%;
|
||||
|
||||
text-align: center;
|
||||
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 0.35em;
|
||||
text-align: justify;
|
||||
vertical-align: top;
|
||||
|
||||
line-height: 1.5em;
|
||||
}
|
||||
}
|
||||
|
||||
table,
|
||||
td {
|
||||
border: 2px solid black;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
}
|
||||
|
||||
.order-anim {
|
||||
&-enter-active,
|
||||
&-leave-active {
|
||||
transition: opacity 150ms ease-in-out;
|
||||
}
|
||||
|
||||
&-enter-from,
|
||||
&-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -36,7 +36,11 @@
|
||||
</td>
|
||||
|
||||
<td colspan="5">
|
||||
<input type="text" v-model="footerInfo.secondaryDispatcherName" placeholder="dyżurny (wypełnić jedno)" />
|
||||
<input
|
||||
type="text"
|
||||
v-model="footerInfo.secondaryDispatcherName"
|
||||
placeholder="dyżurny (wypełnić jedno)"
|
||||
/>
|
||||
<br />
|
||||
z polecenia dyżurnego ruchu
|
||||
</td>
|
||||
@@ -48,8 +52,8 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import orderFooterMixin from '../mixins/orderFooterMixin';
|
||||
import { useStore } from '../store/store';
|
||||
import orderFooterMixin from '../../mixins/orderFooterMixin';
|
||||
import { useStore } from '../../store/store';
|
||||
|
||||
export default defineComponent({
|
||||
mixins: [orderFooterMixin],
|
||||
@@ -59,7 +63,7 @@ export default defineComponent({
|
||||
|
||||
return {
|
||||
store,
|
||||
footerInfo: store.orderFooter,
|
||||
footerInfo: store.orderFooter
|
||||
};
|
||||
},
|
||||
|
||||
@@ -68,9 +72,9 @@ export default defineComponent({
|
||||
deep: true,
|
||||
handler() {
|
||||
this.generateFooter();
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
<template>
|
||||
<section class="order-n">
|
||||
<section class="header" ref="header">
|
||||
<section class="order_header" ref="header">
|
||||
<h2 class="flex-center">
|
||||
Rozkaz pisemny "N" nr
|
||||
<input type="text" v-model="order.header.orderNo" placeholder="nr rozkazu" />
|
||||
Rozkaz pisemny "N" nr
|
||||
<input type="number" v-model="order.header.orderNo" placeholder="nr rozkazu" min="1" />
|
||||
</h2>
|
||||
<div class="flex-row">
|
||||
dla pociągu nr <input type="text" v-model="order.header.trainNo" placeholder="nr pociągu" /> dnia
|
||||
dla pociągu nr
|
||||
<input type="text" v-model="order.header.trainNo" placeholder="nr pociągu" />
|
||||
dnia
|
||||
<input type="text" v-model="order.header.date" placeholder="data" />
|
||||
</div>
|
||||
</section>
|
||||
<section class="table-section">
|
||||
<table class="options-table">
|
||||
|
||||
<section class="order_table-container">
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
@@ -21,10 +24,13 @@
|
||||
</div>
|
||||
</td>
|
||||
<td ref="row-1">
|
||||
Od <input type="text" v-model="order.rows[0].from" holder="stacja / post." /> do
|
||||
<input type="text" v-model="order.rows[0].to" holder="stacja / post." /> tor nr
|
||||
<input type="text" v-model="order.rows[0].trackNo" holder="nr toru" /> jest zamknięty, ruch jednotorowy
|
||||
dwukierunkowy wprowadzono po torze nr
|
||||
Od
|
||||
<input type="text" v-model="order.rows[0].from" holder="stacja / post." />
|
||||
do
|
||||
<input type="text" v-model="order.rows[0].to" holder="stacja / post." />
|
||||
tor nr
|
||||
<input type="text" v-model="order.rows[0].trackNo" holder="nr toru" />
|
||||
jest zamknięty, ruch jednotorowy dwukierunkowy wprowadzono po torze nr
|
||||
<input type="text" v-model="order.rows[0].trackNo2" holder="nr toru" />
|
||||
</td>
|
||||
</tr>
|
||||
@@ -69,7 +75,8 @@
|
||||
v-model="order.rows[1].signal1"
|
||||
holder="nazwa sem."
|
||||
:radio-checked="
|
||||
order.rows[1].checkbox == 'checkbox-2a' && order.rows[1].signalType == 'wyjazdowego'
|
||||
order.rows[1].checkbox == 'checkbox-2a' &&
|
||||
order.rows[1].signalType == 'wyjazdowego'
|
||||
"
|
||||
/>
|
||||
</label>
|
||||
@@ -89,7 +96,8 @@
|
||||
v-model="order.rows[1].signal2"
|
||||
holder="nazwa sem."
|
||||
:radio-checked="
|
||||
order.rows[1].checkbox == 'checkbox-2a' && order.rows[1].signalType == 'drogowskazowego'
|
||||
order.rows[1].checkbox == 'checkbox-2a' &&
|
||||
order.rows[1].signalType == 'drogowskazowego'
|
||||
"
|
||||
/>
|
||||
(odnoszącego się do wyjazdu pociągu)
|
||||
@@ -110,7 +118,8 @@
|
||||
v-model="order.rows[1].signal3"
|
||||
holder="nazwa sem."
|
||||
:radio-checked="
|
||||
order.rows[1].checkbox == 'checkbox-2a' && order.rows[1].signalType == 'wjazdowego'
|
||||
order.rows[1].checkbox == 'checkbox-2a' &&
|
||||
order.rows[1].signalType == 'wjazdowego'
|
||||
"
|
||||
/>
|
||||
na post. odg. bez sem. wyjazdowego
|
||||
@@ -191,15 +200,19 @@
|
||||
<option value="Popychanie">Popychanie</option>
|
||||
</select>
|
||||
pociągu odbędzie się w kierunku:
|
||||
<input type="text" v-model="order.rows[2].direction" holder="stacja / post." /> do km
|
||||
<input type="text" v-model="order.rows[2].toKilometer" holder="kilometry" /> skąd
|
||||
<input type="text" v-model="order.rows[2].direction" holder="stacja / post." />
|
||||
do km
|
||||
<input type="text" v-model="order.rows[2].toKilometer" holder="kilometry" />
|
||||
skąd
|
||||
<select v-model="order.rows[2].option2">
|
||||
<option value="pociąg">pociąg</option>
|
||||
<option value="popychacz">popychacz</option>
|
||||
</select>
|
||||
ma wrócić po torze lewym nr
|
||||
<input type="text" v-model="order.rows[2].trackNo" holder="nr toru" /> najpóźniej o godz.
|
||||
<input type="text" v-model="order.rows[2].untilHour" holder="godzina" /> min.
|
||||
<input type="text" v-model="order.rows[2].trackNo" holder="nr toru" />
|
||||
najpóźniej o godz.
|
||||
<input type="text" v-model="order.rows[2].untilHour" holder="godzina" />
|
||||
min.
|
||||
<input type="text" v-model="order.rows[2].untilMin" holder="minuta" />
|
||||
</td>
|
||||
</tr>
|
||||
@@ -212,13 +225,14 @@
|
||||
</td>
|
||||
<td ref="row-4">
|
||||
<strong>WJAZD</strong> z toru szlakowego nr
|
||||
<input type="text" v-model="order.rows[3].trackNo" holder="nr toru" /> na
|
||||
<input type="text" v-model="order.rows[3].trackNo" holder="nr toru" />
|
||||
na
|
||||
<select v-model="order.rows[3].optionStation">
|
||||
<option value="stację">stację</option>
|
||||
<option value="posterunek odgałęźny">posterunek odgałęźny</option>
|
||||
</select>
|
||||
<input type="text" v-model="order.rows[3].stationName" holder="stacja / post." /> odbędzie się po
|
||||
otrzymaniu:
|
||||
<input type="text" v-model="order.rows[3].stationName" holder="stacja / post." />
|
||||
odbędzie się po otrzymaniu:
|
||||
<div style="margin-top: 0.5rem">
|
||||
<input
|
||||
type="radio"
|
||||
@@ -259,8 +273,10 @@
|
||||
</td>
|
||||
<td ref="row-5">
|
||||
<strong>ZEZWALAM</strong> wjechać z toru szlakowego nr
|
||||
<input type="text" v-model="order.rows[4].trackNo" holder="nr toru" /> z kierunku
|
||||
<input type="text" v-model="order.rows[4].direction" holder="stacja / post." /> na
|
||||
<input type="text" v-model="order.rows[4].trackNo" holder="nr toru" />
|
||||
z kierunku
|
||||
<input type="text" v-model="order.rows[4].direction" holder="stacja / post." />
|
||||
na
|
||||
<select v-model="order.rows[4].stationType">
|
||||
<option value="stację">stację</option>
|
||||
<option value="posterunek odgałęźny">posterunek odgałęźny</option>
|
||||
@@ -270,6 +286,46 @@
|
||||
<input type="text" v-model="order.rows[4].on" holder="nazwa sygnału" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr style="height: 270px">
|
||||
<td>
|
||||
<label for="row-enabled-6">6</label>
|
||||
<div>
|
||||
<input type="checkbox" id="row-enabled-6" v-model="order.rows[5].enabled" />
|
||||
</div>
|
||||
</td>
|
||||
<td ref="row-6">
|
||||
<button
|
||||
class="g-button text"
|
||||
@click="order.rows[5].twoWay.enabled = !order.rows[5].twoWay.enabled"
|
||||
>
|
||||
>
|
||||
<span v-if="!order.rows[5].twoWay.enabled">
|
||||
Wygeneruj treść na wprowadzenie ruchu dwukierunkowego
|
||||
</span>
|
||||
<span v-else>Wpisz treść własnoręcznie</span>
|
||||
</button>
|
||||
|
||||
<div>Inne:</div>
|
||||
|
||||
<div v-if="order.rows[5].twoWay.enabled">
|
||||
od
|
||||
<input type="text" v-model="order.rows[5].twoWay.from" holder="stacja / post." />
|
||||
do
|
||||
<input type="text" v-model="order.rows[5].twoWay.to" holder="stacja / post." />
|
||||
po torze nr
|
||||
<input type="text" v-model="order.rows[5].twoWay.trackNo" holder="nr toru" />
|
||||
wprowadzono ruch dwukierunkowy.
|
||||
</div>
|
||||
|
||||
<textarea
|
||||
v-else
|
||||
class="others"
|
||||
cols="30"
|
||||
rows="10"
|
||||
v-model="order.rows[5].content"
|
||||
></textarea>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
@@ -278,8 +334,8 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, reactive } from 'vue';
|
||||
import { useStore } from '../store/store';
|
||||
import { handleOrderPlaceholders } from '../handlers/orderPlaceholderHandler';
|
||||
import { handleOrderPlaceholders } from '../../handlers/orderPlaceholderHandler';
|
||||
import { useStore } from '../../store/store';
|
||||
|
||||
type TOrderRows = 1 | 2 | 3 | 4 | 5;
|
||||
|
||||
@@ -294,7 +350,7 @@ export default defineComponent({
|
||||
() => {
|
||||
const { header } = order;
|
||||
|
||||
const message = `<i>Rozkaz pisemny "N" nr ${header.orderNo || '_'} dla pociągu nr ${
|
||||
const message = `\n<i><b>Rozkaz pisemny "N" nr ${header.orderNo || '_'}</b> dla pociągu nr ${
|
||||
header.trainNo || '_'
|
||||
} dnia ${header.date}</i>`;
|
||||
|
||||
@@ -306,7 +362,9 @@ export default defineComponent({
|
||||
|
||||
const message = `Od ${row.from || '_'} do ${row.to || '_'} tor nr ${
|
||||
row.trackNo || '_'
|
||||
} jest zamknięty, ruch jednotorowy dwukierunkowy wprowadzono po torze nr ${row.trackNo2 || '_'}`;
|
||||
} jest zamknięty, ruch jednotorowy dwukierunkowy wprowadzono po torze nr ${
|
||||
row.trackNo2 || '_'
|
||||
}`;
|
||||
|
||||
return message;
|
||||
},
|
||||
@@ -317,20 +375,25 @@ export default defineComponent({
|
||||
let message = `ZEZWALAM po otrzymaniu ${row.option1 || '_'}`;
|
||||
|
||||
if (row.checkbox == 'checkbox-2a') {
|
||||
message += ` przejechać obok wskazującego sygnał "Stój" semafora ${row.signalType || '_'} `;
|
||||
message += ` przejechać obok wskazującego sygnał "Stój" semafora ${
|
||||
row.signalType || '_'
|
||||
} `;
|
||||
|
||||
if (row.signalType == 'wyjazdowego') message += row.signal1 || '_';
|
||||
if (row.signalType == 'drogowskazowego')
|
||||
message += `${row.signal2 || '_'} (odnoszącego się do wyjazdu pociągu)`;
|
||||
if (row.signalType == 'wjazdowego') message += `${row.signal3 || '_'} na post. odg. bez sem. wyjazdowego`;
|
||||
if (row.signalType == 'wjazdowego')
|
||||
message += `${row.signal3 || '_'} na post. odg. bez sem. wyjazdowego`;
|
||||
|
||||
message += ` i wyjechać w kierunku ${row.direction1 || '_'} na tor szlakowy ${row.option2 || '_'} nr ${
|
||||
row.trackNoTo1 || '_'
|
||||
}`;
|
||||
message += ` i wyjechać w kierunku ${row.direction1 || '_'} na tor szlakowy ${
|
||||
row.option2 || '_'
|
||||
} nr ${row.trackNoTo1 || '_'}`;
|
||||
}
|
||||
|
||||
if (row.checkbox == 'checkbox-2b') {
|
||||
message += ` z toru nr ${row.trackNoFrom || '_'} nie posiadającego semafora wyjazdowego wyjechać w kierunku ${
|
||||
message += ` z toru nr ${
|
||||
row.trackNoFrom || '_'
|
||||
} nie posiadającego semafora wyjazdowego wyjechać w kierunku ${
|
||||
row.direction2 || '_'
|
||||
} na tor szlakowy ${row.option3 || '_'} nr ${row.trackNoTo2 || '_'}`;
|
||||
}
|
||||
@@ -341,11 +404,11 @@ export default defineComponent({
|
||||
() => {
|
||||
const row = order.rows[2];
|
||||
|
||||
let message = `${row.option1 || '_'} pociągu odbędzie się w kierunku: ${row.direction || '_'} do km ${
|
||||
row.toKilometer || '_'
|
||||
} skąd ${row.option2 || '_'} ma wrócić po torze lewym nr ${row.trackNo || '_'} najpóźniej o godz. ${
|
||||
row.untilHour || '_'
|
||||
} min. ${row.untilMin || '_'}`;
|
||||
let message = `${row.option1 || '_'} pociągu odbędzie się w kierunku: ${
|
||||
row.direction || '_'
|
||||
} do km ${row.toKilometer || '_'} skąd ${row.option2 || '_'} ma wrócić po torze lewym nr ${
|
||||
row.trackNo || '_'
|
||||
} najpóźniej o godz. ${row.untilHour || '_'} min. ${row.untilMin || '_'}`;
|
||||
|
||||
return message;
|
||||
},
|
||||
@@ -353,15 +416,18 @@ export default defineComponent({
|
||||
() => {
|
||||
const row = order.rows[3];
|
||||
|
||||
let message = `WJAZD z toru szlakowego nr ${row.trackNo || '_'} na ${row.optionStation || '_'} ${
|
||||
row.stationName || '_'
|
||||
} odbędzie się po otrzymaniu: `;
|
||||
let message = `WJAZD z toru szlakowego nr ${row.trackNo || '_'} na ${
|
||||
row.optionStation || '_'
|
||||
} ${row.stationName || '_'} odbędzie się po otrzymaniu: `;
|
||||
|
||||
if (row.checkbox == 'checkbox-4a')
|
||||
message += `sygnału zastępczego "Sz" na osobnym urządzeniu ustawionym z ${row.side || '_'} strony toru`;
|
||||
message += `sygnału zastępczego "Sz" na osobnym urządzeniu ustawionym z ${
|
||||
row.side || '_'
|
||||
} strony toru`;
|
||||
|
||||
if (row.checkbox == 'checkbox-4b')
|
||||
message += 'rozkazu pisemnego "N" (doręczonego lub przekazanego przez urządzenia łączności)';
|
||||
message +=
|
||||
'rozkazu pisemnego "N" (doręczonego lub przekazanego przez urządzenia łączności)';
|
||||
|
||||
return message;
|
||||
},
|
||||
@@ -371,16 +437,29 @@ export default defineComponent({
|
||||
|
||||
const message = `ZEZWALAM wjechać z toru szlakowego nr ${row.trackNo || '_'} z kierunku ${
|
||||
row.direction || '_'
|
||||
} na ${row.stationType || '_'} ${row.stationName || '_'} i przejechać obok sygnału "Stój" na ${row.on || '_'} `;
|
||||
} na ${row.stationType || '_'} ${
|
||||
row.stationName || '_'
|
||||
} i przejechać obok sygnału "Stój" na ${row.on || '_'} `;
|
||||
|
||||
return message;
|
||||
},
|
||||
|
||||
() => {
|
||||
const row = order.rows[5];
|
||||
|
||||
if (row.twoWay.enabled)
|
||||
return `Inne: od ${row.twoWay.from || '_'} do ${row.twoWay.to || '_'} po torze nr ${
|
||||
row.twoWay.trackNo || '_'
|
||||
} wprowadzono ruch dwukierunkowy.`;
|
||||
|
||||
return 'Inne: ' + row.content;
|
||||
}
|
||||
];
|
||||
|
||||
return {
|
||||
store,
|
||||
order,
|
||||
rowMethods,
|
||||
rowMethods
|
||||
};
|
||||
},
|
||||
|
||||
@@ -389,14 +468,14 @@ export default defineComponent({
|
||||
deep: true,
|
||||
handler() {
|
||||
this.generateMessage();
|
||||
},
|
||||
}
|
||||
},
|
||||
'order.rows': {
|
||||
deep: true,
|
||||
handler() {
|
||||
this.updatePlaceholders();
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
@@ -426,15 +505,14 @@ export default defineComponent({
|
||||
generateMessage() {
|
||||
let message = this.rowMethods[0]();
|
||||
|
||||
for (let i = 0; i < 5; i++) {
|
||||
for (let i = 0; i < this.order.rows.length; i++) {
|
||||
if (!this.order.rows[i].enabled) continue;
|
||||
|
||||
message += ` <b> [ ${i + 1} ] </b> ${this.rowMethods[i + 1]()}`;
|
||||
message += `\n--------\n<b>[ ${i + 1} ]</b> ${this.rowMethods[i + 1]()}`;
|
||||
}
|
||||
|
||||
this.store.orderMessage = message;
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
<section class="order-o">
|
||||
<section class="order_header">
|
||||
<h2 class="flex-center" style="padding: 0 0.5em">
|
||||
Rozkaz pisemny "O" nr
|
||||
<input type="text" v-model="order.header.orderNo" placeholder="nr rozkazu"/>
|
||||
Rozkaz pisemny "O" nr
|
||||
<input type="number" v-model="order.header.orderNo" placeholder="nr rozkazu" min="1" />
|
||||
</h2>
|
||||
|
||||
<div class="flex-row" style="padding: 0 0.5em">
|
||||
dla pociągu nr <input type="text" v-model="order.header.trainNo" placeholder="nr pociągu" /> dnia
|
||||
dla pociągu nr
|
||||
<input type="text" v-model="order.header.trainNo" placeholder="nr pociągu" />
|
||||
dnia
|
||||
<input type="text" v-model="order.header.date" />
|
||||
</div>
|
||||
|
||||
@@ -41,7 +43,7 @@
|
||||
<td colspan="2">kilometra</td>
|
||||
</tr>
|
||||
|
||||
<tr v-for="row in order.orderList" class="tr-data">
|
||||
<tr v-for="(row, i) in order.orderList" :key="i" class="tr-data">
|
||||
<td>
|
||||
<textarea v-model="row.name"></textarea>
|
||||
</td>
|
||||
@@ -63,19 +65,19 @@
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="order_other">
|
||||
<span><b>2.</b> Inne:</span>
|
||||
<br />
|
||||
<textarea v-model="order.other"></textarea>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="order_other">
|
||||
<span><b>2.</b> Inne:</span>
|
||||
<br />
|
||||
<textarea class="others" cols="30" rows="10" v-model="order.other"></textarea>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, reactive } from 'vue';
|
||||
import { useStore } from '../store/store';
|
||||
import { useStore } from '../../store/store';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'OrderO',
|
||||
@@ -88,16 +90,16 @@ export default defineComponent({
|
||||
() => {
|
||||
const { header } = order;
|
||||
|
||||
return `<i>Rozkaz pisemny "O" nr ${header.orderNo || '_'} dla pociągu nr ${header.trainNo || '_'} dnia ${
|
||||
header.date || '_'
|
||||
}</i>`;
|
||||
},
|
||||
return `\n<i><b>Rozkaz pisemny "O" nr ${header.orderNo || '_'}</b> dla pociągu nr ${
|
||||
header.trainNo || '_'
|
||||
} dnia ${header.date || '_'}</i>`;
|
||||
}
|
||||
];
|
||||
|
||||
return {
|
||||
store,
|
||||
order,
|
||||
rowMethods,
|
||||
rowMethods
|
||||
};
|
||||
},
|
||||
|
||||
@@ -110,40 +112,37 @@ export default defineComponent({
|
||||
deep: true,
|
||||
handler() {
|
||||
this.generateMessage();
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
generateMessage() {
|
||||
let message = this.rowMethods[0]();
|
||||
|
||||
if (this.order.orderList.some((row) => row.name)) message += `<b> [ 1 ] </b>`;
|
||||
|
||||
const rowsMessageList = [];
|
||||
if (this.order.orderList.some((row) => row.name)) {
|
||||
message += `\n--------\n<b>[ 1 ]</b>`;
|
||||
message += '\n1) zmniejszyć prędkość jazdy i zachować ostrożność';
|
||||
message += '\n2) jechać ostrożnie (j.o.)\n';
|
||||
}
|
||||
|
||||
for (let i = 0; i < this.order.orderList.length; i++) {
|
||||
const row = this.order.orderList[i];
|
||||
if (!row.name) continue;
|
||||
|
||||
let rowMessage = '';
|
||||
rowMessage += ` ${row.name || '_'} od ${row.from || '_'} do ${row.to || '_'} kilometra`;
|
||||
message += `\n- ${row.name || '_'} od ${row.from || '_'} do ${row.to || '_'} kilometra`;
|
||||
|
||||
if (row.vmax) rowMessage += ` prędkość najwyżej ${row.vmax} km/h`;
|
||||
if (row.jo) rowMessage += ` jechać ostrożnie`;
|
||||
if (row.vmax) message += ` prędkość najwyżej ${row.vmax} km/h`;
|
||||
if (row.jo) message += ` jechać ostrożnie`;
|
||||
|
||||
rowMessage += ` z powodu: ${row.reason || '_'}`;
|
||||
|
||||
rowsMessageList.push(rowMessage);
|
||||
message += ` z powodu: ${row.reason || '_'}`;
|
||||
}
|
||||
|
||||
message += rowsMessageList.join("; ");
|
||||
|
||||
if (this.order.other) message += ` <b> [ 2 ] </b> Inne: ${this.order.other}`;
|
||||
if (this.order.other) message += `\n--------\n<b>[ 2 ]</b> Inne: ${this.order.other}`;
|
||||
|
||||
this.store.orderMessage = message;
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -157,16 +156,6 @@ th {
|
||||
|
||||
.order_header {
|
||||
padding: 0.5em 0;
|
||||
border: 2px solid black;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.horizontal-bar {
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
|
||||
background-color: black;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
|
||||
.order_table {
|
||||
@@ -187,10 +176,12 @@ th {
|
||||
input {
|
||||
width: 80%;
|
||||
}
|
||||
}
|
||||
|
||||
.order_table {
|
||||
textarea {
|
||||
width: 80%;
|
||||
height: 40px;
|
||||
width: 90%;
|
||||
min-height: 50px;
|
||||
resize: vertical;
|
||||
}
|
||||
}
|
||||
@@ -201,15 +192,7 @@ th {
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 285px;
|
||||
|
||||
padding: 0.5em;
|
||||
|
||||
textarea {
|
||||
resize: vertical;
|
||||
height: 220px;
|
||||
width: 95%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<section class="order-s">
|
||||
<section class="header">
|
||||
<section class="order_header">
|
||||
<h2 class="flex-center">
|
||||
Rozkaz pisemny "S" nr
|
||||
<input type="text" v-model="order.header.orderNo" placeholder="nr rozkazu" />
|
||||
Rozkaz pisemny "S" nr
|
||||
<input type="number" v-model="order.header.orderNo" placeholder="nr rozkazu" min="1" />
|
||||
</h2>
|
||||
<div class="flex-row">
|
||||
dla
|
||||
@@ -11,12 +11,15 @@
|
||||
<option value="pociągu">pociągu</option>
|
||||
<option value="manewru">manewru</option>
|
||||
</select>
|
||||
nr <input type="text" v-model="order.header.trainNo" :placeholder="`nr ${order.header.for}`" /> dnia
|
||||
nr
|
||||
<input type="text" v-model="order.header.trainNo" :placeholder="`nr ${order.header.for}`" />
|
||||
dnia
|
||||
<input type="text" v-model="order.header.date" placeholder="data" />
|
||||
</div>
|
||||
</section>
|
||||
<section class="table-section">
|
||||
<table class="options-table">
|
||||
|
||||
<section class="order_table-container">
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
@@ -57,6 +60,9 @@
|
||||
holder="nazwa sem."
|
||||
:radio-checked="order.rows[0].radio1 == 'radio-1a-1'"
|
||||
/>
|
||||
<span v-if="order.rows[0].optionSignal == 'drogowskazowego'">
|
||||
(odnoszącego się do wyjazdu pociągu)</span
|
||||
>
|
||||
<br />
|
||||
</label>
|
||||
<hr />
|
||||
@@ -130,7 +136,7 @@
|
||||
holder="nazwa sem."
|
||||
:radio-checked="order.rows[1].signalType == 'drogowskazowego'"
|
||||
/>
|
||||
(odnoszącego się do wyjazdu pociągu)
|
||||
(odnoszącego się do wjazdu pociągu)
|
||||
</label>
|
||||
<br />
|
||||
<input
|
||||
@@ -184,17 +190,23 @@
|
||||
</div>
|
||||
</td>
|
||||
<td ref="row-3">
|
||||
Od <input type="text" v-model="order.rows[2].from" holder="stacja / post." /> do
|
||||
<input type="text" v-model="order.rows[2].to" holder="stacja / post." /> po torze nr
|
||||
<input type="text" v-model="order.rows[2].trackNo" holder="nr toru" /> ruch pociągów prowadzony jest w
|
||||
odstępie posterunków następczych. Wskazania semaforów sbl są nieważne. Zachować ostrożność od ostatniego
|
||||
semafora ze wskaźnikiem "W18". Szlak wolny, ostatni pociąg nr
|
||||
<input type="text" v-model="order.rows[2].trainNo" holder="nr pociągu" /> przybył do
|
||||
<input type="text" v-model="order.rows[2].arrivedTo" holder="stacja / post." /> o godzinie
|
||||
Od
|
||||
<input type="text" v-model="order.rows[2].from" holder="stacja / post." />
|
||||
do
|
||||
<input type="text" v-model="order.rows[2].to" holder="stacja / post." />
|
||||
po torze nr
|
||||
<input type="text" v-model="order.rows[2].trackNo" holder="nr toru" />
|
||||
ruch pociągów prowadzony jest w odstępie posterunków następczych. Wskazania semaforów
|
||||
sbl są nieważne. Zachować ostrożność od ostatniego semafora ze wskaźnikiem "W18".
|
||||
Szlak wolny, ostatni pociąg nr
|
||||
<input type="text" v-model="order.rows[2].trainNo" holder="nr pociągu" />
|
||||
przybył do
|
||||
<input type="text" v-model="order.rows[2].arrivedTo" holder="stacja / post." />
|
||||
o godzinie
|
||||
<input type="text" v-model="order.rows[2].hour" holder="godzina" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr style="height: 255px">
|
||||
<tr style="height: 270px">
|
||||
<td>
|
||||
<label for="row-enabled-4">4</label>
|
||||
<div>
|
||||
@@ -207,9 +219,63 @@
|
||||
</div>
|
||||
</td>
|
||||
<td ref="row-4">
|
||||
Inne:
|
||||
<br />
|
||||
<textarea id="" cols="30" rows="10" v-model="order.rows[3].content"></textarea>
|
||||
<button
|
||||
class="g-button text"
|
||||
@click="order.rows[3].w5.enabled = !order.rows[3].w5.enabled"
|
||||
>
|
||||
>
|
||||
<span v-if="!order.rows[3].w5.enabled"
|
||||
>Wygeneruj treść na pominięcie wskaźnika W5</span
|
||||
>
|
||||
<span v-else>Wpisz treść własnoręcznie</span>
|
||||
</button>
|
||||
|
||||
<div>Inne:</div>
|
||||
<div v-if="order.rows[3].w5.enabled">
|
||||
zezwalam na wyjazd poza
|
||||
<select id="select-borderType" v-model="order.rows[3].w5.borderType">
|
||||
<option value="wskaźnik przetaczania W5">wskaźnik przetaczania W5</option>
|
||||
<option value="granicę przetaczania">granicę przetaczania</option>
|
||||
</select>
|
||||
po torze szlakowym nr
|
||||
<input type="text" v-model="order.rows[3].w5.trackNo" holder="nr szlaku" />
|
||||
do kilometra
|
||||
<input type="text" v-model="order.rows[3].w5.maxKm" holder="km szlaku" />. Powrót
|
||||
odbędzie się na
|
||||
<select
|
||||
id="select-returnWay"
|
||||
v-model="order.rows[3].w5.returnWay"
|
||||
style="width: 250px"
|
||||
>
|
||||
<option :value="`sygnał ręczny "Do mnie"`">
|
||||
sygnał ręczny "Do mnie"
|
||||
</option>
|
||||
<option
|
||||
:value="`sygnał "Do mnie" przekazany przez urządzenia radiołączności`"
|
||||
>
|
||||
sygnał "Do mnie" przekazany przez urządzenia radiołączności
|
||||
</option>
|
||||
<option value="sygnał Ms2 podany na tarczy manewrowej">
|
||||
sygnał Ms2 podany na tarczy manewrowej
|
||||
</option>
|
||||
</select>
|
||||
<input
|
||||
type="text"
|
||||
v-model="order.rows[3].w5.tmName"
|
||||
holder="nazwa tarczy"
|
||||
v-if="order.rows[3].w5.returnWay.includes('tarczy')"
|
||||
/>
|
||||
do godziny
|
||||
<input type="text" v-model="order.rows[3].w5.maxHour" holder="godzina" />
|
||||
</div>
|
||||
|
||||
<textarea
|
||||
v-else
|
||||
class="others"
|
||||
cols="30"
|
||||
rows="10"
|
||||
v-model="order.rows[3].content"
|
||||
></textarea>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@@ -220,8 +286,8 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, reactive } from 'vue';
|
||||
import { handleOrderPlaceholders } from '../handlers/orderPlaceholderHandler';
|
||||
import { useStore } from '../store/store';
|
||||
import { handleOrderPlaceholders } from '../../handlers/orderPlaceholderHandler';
|
||||
import { useStore } from '../../store/store';
|
||||
|
||||
type TOrderRows = 1 | 2 | 3 | 4;
|
||||
|
||||
@@ -236,7 +302,7 @@ export default defineComponent({
|
||||
() => {
|
||||
const { header } = order;
|
||||
|
||||
return `<i>Rozkaz pisemny "S" nr ${header.orderNo || '_'} dla ${header.for || '_'} nr ${
|
||||
return `\n<i><b>Rozkaz pisemny "S" nr ${header.orderNo || '_'}</b> dla ${header.for || '_'} nr ${
|
||||
header.trainNo || '_'
|
||||
} dnia ${header.date || '_'}</i>`;
|
||||
},
|
||||
@@ -247,10 +313,13 @@ export default defineComponent({
|
||||
let message = `zezwalam po otrzymaniu ${row.option1 || '_'}`;
|
||||
|
||||
if (row.radio1 == 'radio-1a-1')
|
||||
message += ` przejechać obok wskazującego sygnał "Stój" semafora ${row.optionSignal || '_'} ${
|
||||
row.signal1 || '_'
|
||||
}`;
|
||||
else message += ` wyjechać z toru nr ${row.trackNo || '_'} nie posiadającego semafora wyjazdowego`;
|
||||
message += ` przejechać obok wskazującego sygnał "Stój" semafora ${
|
||||
row.optionSignal || '_'
|
||||
} ${row.signal1 || '_'}${row.optionSignal == 'drogowskazowego' ? ' (odnoszącego się do wyjazdu pociągu)' : ''}`;
|
||||
else
|
||||
message += ` wyjechać z toru nr ${
|
||||
row.trackNo || '_'
|
||||
} nie posiadającego semafora wyjazdowego`;
|
||||
|
||||
return message;
|
||||
},
|
||||
@@ -265,13 +334,15 @@ export default defineComponent({
|
||||
message += `wjazdowego ${row.signal1 || '_'}`;
|
||||
break;
|
||||
case 'drogowskazowego':
|
||||
message += `drogowskazowego ${row.signal2 || '_'}`;
|
||||
message += `drogowskazowego ${row.signal2 || '_'} (odnoszącego się do wjazdu pociągu)`;
|
||||
break;
|
||||
case 'odstępowego':
|
||||
message += `odstępowego ${row.signal3 || '_'}`;
|
||||
break;
|
||||
case 'toru':
|
||||
message += `wjechać z zamkniętego toru nr ${row.trackNo || '_'} nie posiadającego semafora wjazdowego`;
|
||||
message += `wjechać z zamkniętego toru nr ${
|
||||
row.trackNo || '_'
|
||||
} nie posiadającego semafora wjazdowego`;
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -293,18 +364,37 @@ export default defineComponent({
|
||||
() => {
|
||||
const row = order.rows[3];
|
||||
|
||||
if (row.w5.enabled) {
|
||||
const { borderType, trackNo, maxHour, maxKm, returnWay, tmName } = row.w5;
|
||||
const textArray = [];
|
||||
|
||||
textArray.push(
|
||||
'Inne: zezwalam na wyjazd poza',
|
||||
borderType || '_',
|
||||
'po torze szlakowym nr',
|
||||
trackNo || '_'
|
||||
);
|
||||
if (maxKm) textArray.push(`do kilometra ${maxKm}`);
|
||||
textArray.push('.');
|
||||
textArray.push('Powrót odbędzie się na', returnWay || '_');
|
||||
if (returnWay.includes('tarczy')) textArray.push(tmName || '_');
|
||||
textArray.push(`do godziny ${maxHour || '_'}`);
|
||||
|
||||
return textArray.join(' ').replace(/ \./, '.');
|
||||
}
|
||||
|
||||
return `Inne: ${row.content}`;
|
||||
},
|
||||
}
|
||||
];
|
||||
|
||||
return {
|
||||
store,
|
||||
order,
|
||||
rowMethods,
|
||||
rowMethods
|
||||
};
|
||||
},
|
||||
|
||||
activated() {
|
||||
activated() {
|
||||
this.generateMessage();
|
||||
},
|
||||
|
||||
@@ -317,15 +407,15 @@ activated() {
|
||||
deep: true,
|
||||
handler() {
|
||||
this.generateMessage();
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
'order.rows': {
|
||||
deep: true,
|
||||
handler() {
|
||||
this.updatePlaceholders();
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
@@ -335,13 +425,13 @@ activated() {
|
||||
});
|
||||
},
|
||||
|
||||
generateMessage() {
|
||||
generateMessage() {
|
||||
let message = this.rowMethods[0]();
|
||||
|
||||
for (let i = 0; i < 4; i++) {
|
||||
if (!this.order.rows[i].enabled) continue;
|
||||
|
||||
message += ` <b> [ ${i + 1} ] </b> ${this.rowMethods[i + 1]()}`;
|
||||
message += `\n--------\n<b>[ ${i + 1} ]</b> ${this.rowMethods[i + 1]()}`;
|
||||
}
|
||||
|
||||
this.store.orderMessage = message;
|
||||
@@ -354,16 +444,7 @@ activated() {
|
||||
this.$nextTick(() => {
|
||||
handleOrderPlaceholders(isRowEnabled, rowRef);
|
||||
});
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
textarea {
|
||||
width: 95%;
|
||||
height: 200px;
|
||||
resize: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { useStore } from '../store/store';
|
||||
import { useStore } from '../../store/store';
|
||||
|
||||
export default defineComponent({
|
||||
data() {
|
||||
@@ -31,22 +31,22 @@ export default defineComponent({
|
||||
orderTypeList: [
|
||||
{
|
||||
id: 'orderN',
|
||||
name: 'N',
|
||||
name: 'N'
|
||||
},
|
||||
{
|
||||
id: 'orderS',
|
||||
name: 'S',
|
||||
name: 'S'
|
||||
},
|
||||
{
|
||||
id: 'orderO',
|
||||
name: 'O',
|
||||
},
|
||||
],
|
||||
name: 'O'
|
||||
}
|
||||
]
|
||||
};
|
||||
},
|
||||
setup() {
|
||||
return {
|
||||
store: useStore(),
|
||||
store: useStore()
|
||||
};
|
||||
},
|
||||
|
||||
@@ -55,27 +55,17 @@ export default defineComponent({
|
||||
if (type != this.store.chosenOrderType) this.store.chosenLocalOrderId = '';
|
||||
|
||||
this.store.chosenOrderType = type;
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../styles/global.scss';
|
||||
|
||||
.sidebar {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
||||
z-index: 999;
|
||||
|
||||
transform: translate(-100%, 0);
|
||||
}
|
||||
@use '../../styles/colors';
|
||||
|
||||
.sidebar_content {
|
||||
display: grid;
|
||||
grid-template-rows: repeat(3, 1fr);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25em;
|
||||
|
||||
font-size: 1.5em;
|
||||
@@ -91,7 +81,7 @@ export default defineComponent({
|
||||
align-items: center;
|
||||
|
||||
color: white;
|
||||
background-color: #1d1d1d;
|
||||
background-color: colors.$bgColDarker;
|
||||
width: 50px;
|
||||
height: 85px;
|
||||
|
||||
@@ -110,7 +100,7 @@ export default defineComponent({
|
||||
&[data-selected='true'] .bar {
|
||||
transform: translateX(0);
|
||||
|
||||
background-color: $accentCol;
|
||||
background-color: colors.$accentCol;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
@@ -132,22 +122,13 @@ button.option-save {
|
||||
}
|
||||
|
||||
&[data-selected='true'] {
|
||||
background-color: $accentCol;
|
||||
background-color: colors.$accentCol;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 650px) {
|
||||
.sidebar {
|
||||
left: 50%;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
|
||||
transform: translate(-50%, -100%);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1150px) {
|
||||
.sidebar_content {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-direction: row;
|
||||
|
||||
& > button {
|
||||
height: 40px;
|
||||
@@ -161,4 +142,3 @@ button.option-save {
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
<template>
|
||||
<section class="order-list">
|
||||
<h3>{{ $t('order-list.title') }} ({{ localOrderList.length }})</h3>
|
||||
|
||||
<transition-group name="list" tag="ul">
|
||||
<li class="no-orders-warning" v-if="sortedOrderList.length == 0" :key="-1">
|
||||
{{ $t('order-list.no-saved-orders') }}
|
||||
</li>
|
||||
|
||||
<li
|
||||
v-for="order in sortedOrderList"
|
||||
:selected="order.id == store.chosenLocalOrderId"
|
||||
:key="order.id"
|
||||
>
|
||||
<b class="text--accent">#{{ order.id.split('-')[1] }} </b>
|
||||
<b>
|
||||
{{
|
||||
$t('order-list.order-title', {
|
||||
orderName: getOrderName(order.orderType),
|
||||
orderNo: order.orderBody['header']['orderNo'],
|
||||
trainNo: order.orderBody['header']['trainNo']
|
||||
})
|
||||
}}
|
||||
</b>
|
||||
<span
|
||||
v-if="!order.orderVersion || order.orderVersion != ORDER_VERSION"
|
||||
class="wrong-order-indicator"
|
||||
tabindex="0"
|
||||
data-tooltip="Przestarzała wersja rozkazu! Może generować złe informacje!"
|
||||
>⚠
|
||||
</span>
|
||||
<br />
|
||||
{{ $t(`order-list.order-${order.createdAt ? 'added' : 'updated'}`) }}
|
||||
{{ new Date(order.createdAt || order.updatedAt || 0).toLocaleString('pl-PL') }}
|
||||
|
||||
<hr />
|
||||
|
||||
<div class="buttons">
|
||||
<button class="g-button" @click="selectLocalOrder(order)">
|
||||
{{ $t('order-list.button-order-select') }}
|
||||
</button>
|
||||
<button class="g-button" @click="removeOrder(order)">
|
||||
{{ $t('order-list.button-order-remove') }}
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
</transition-group>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import orderStorageMixin from '../../mixins/orderStorageMixin';
|
||||
import { useStore } from '../../store/store';
|
||||
import { LocalStorageOrder } from '../../types/orderTypes';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'OrderList',
|
||||
mixins: [orderStorageMixin],
|
||||
|
||||
data() {
|
||||
return {
|
||||
localOrderList: [] as LocalStorageOrder[],
|
||||
ORDER_VERSION: import.meta.env['VITE_APP_ORDER_VERSION']
|
||||
};
|
||||
},
|
||||
|
||||
setup() {
|
||||
return {
|
||||
store: useStore(),
|
||||
localStorage: window.localStorage
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
getOrderName(orderType: string) {
|
||||
return orderType.split('order')[1];
|
||||
},
|
||||
|
||||
removeOrder(order: LocalStorageOrder) {
|
||||
if (!order) return;
|
||||
|
||||
this.removeLocalOrder(order);
|
||||
this.localOrderList = this.localOrderList.filter((o) => o.id != order.id);
|
||||
|
||||
if (this.localOrderList.length == 0) this.saveOrderSetting('orderCount', 0);
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
sortedOrderList() {
|
||||
return this.localOrderList
|
||||
.slice()
|
||||
.sort((a, b) => (b.createdAt || b.updatedAt!) - (a.createdAt || a.updatedAt!));
|
||||
}
|
||||
},
|
||||
|
||||
activated() {
|
||||
const localStorage = window.localStorage;
|
||||
const orderList = [];
|
||||
|
||||
for (let key in localStorage) {
|
||||
if (!/^order-/g.test(key)) continue;
|
||||
|
||||
const orderObj: LocalStorageOrder = JSON.parse(localStorage[key]);
|
||||
if (!orderObj) continue;
|
||||
|
||||
orderList.push(orderObj);
|
||||
}
|
||||
|
||||
this.localOrderList = orderList;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@use '../../styles/colors';
|
||||
|
||||
.list {
|
||||
&-move,
|
||||
&-enter-active,
|
||||
&-leave-active {
|
||||
transition: all 250ms ease;
|
||||
}
|
||||
&-enter-from,
|
||||
&-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateY(30px);
|
||||
}
|
||||
|
||||
&-leave-active {
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
|
||||
.order-list {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: 1px solid #aaa;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
ul {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
h3 {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
|
||||
margin: 0;
|
||||
margin-bottom: 1em;
|
||||
text-align: center;
|
||||
|
||||
background-color: colors.$bgColDarker;
|
||||
padding: 1em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
li {
|
||||
text-align: left;
|
||||
padding: 1em;
|
||||
margin: 0.5em;
|
||||
background-color: colors.$bgColDarker;
|
||||
|
||||
cursor: pointer;
|
||||
|
||||
&[selected='true'] {
|
||||
outline: 1px solid colors.$accentCol;
|
||||
}
|
||||
|
||||
&.no-orders-warning {
|
||||
text-align: center;
|
||||
font-size: 1.2em;
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
|
||||
.wrong-order-indicator {
|
||||
color: colors.$accentCol;
|
||||
padding: 0 0.25em;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: flex;
|
||||
gap: 0.5em;
|
||||
|
||||
button {
|
||||
padding: 0.5em;
|
||||
background-color: colors.$bgColLighter;
|
||||
|
||||
&:hover {
|
||||
background-color: #666;
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
outline: 2px solid colors.$accentCol;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,335 @@
|
||||
<template>
|
||||
<section class="order-message">
|
||||
<h3>{{ $t('order-message.title') }}</h3>
|
||||
|
||||
<div class="message_body" v-html="orderMessagePreview"></div>
|
||||
<p class="message_info">
|
||||
{{ $t('order-message.info') }}
|
||||
</p>
|
||||
|
||||
<div class="message_actions">
|
||||
<button class="g-button action icon" @click="saveOrder">
|
||||
<LucideSave />
|
||||
{{ $t('order-message.button-save') }}
|
||||
</button>
|
||||
|
||||
<button class="g-button action icon" @click="copyMessage">
|
||||
<LucideCopy />
|
||||
{{ $t('order-message.button-copy') }}
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="g-button action icon"
|
||||
:data-disabled="!store.chosenLocalOrderId"
|
||||
@click="updateOrder"
|
||||
>
|
||||
<LucidePencil />
|
||||
{{ $t('order-message.button-update') }}
|
||||
<span class="text--accent" v-if="store.chosenLocalOrderId"
|
||||
>#{{ store.chosenLocalOrderId.split('-')[1] }}
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<button class="g-button action icon" @click="resetOrder">
|
||||
<LucideRotateCcw />
|
||||
{{ $t('order-message.button-reset') }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="message_checkboxes">
|
||||
<label for="copy-increment" class="g-checkbox">
|
||||
<input
|
||||
type="checkbox"
|
||||
name="copy-increment"
|
||||
id="copy-increment"
|
||||
v-model="incrementOnCopy"
|
||||
@change="onCheckboxChange"
|
||||
/>
|
||||
<span>{{ $t('order-options.update-number-on-copy') }}</span>
|
||||
</label>
|
||||
|
||||
<label for="save-increment" class="g-checkbox">
|
||||
<input
|
||||
type="checkbox"
|
||||
name="save-increment"
|
||||
id="save-increment"
|
||||
v-model="incrementOnSave"
|
||||
@change="onCheckboxChange"
|
||||
/>
|
||||
<span>{{ $t('order-options.update-number-on-save') }}</span>
|
||||
</label>
|
||||
|
||||
<label for="update-date" class="g-checkbox">
|
||||
<input
|
||||
type="checkbox"
|
||||
name="update-date"
|
||||
id="update-date"
|
||||
v-model="updateDate"
|
||||
@change="onCheckboxChange"
|
||||
/>
|
||||
<span>{{ $t('order-options.update-hours') }}</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<transition name="monit-anim">
|
||||
<div class="action_monit" v-if="actionMonit" v-html="actionMonit"></div>
|
||||
</transition>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import orderStorageMixin from '../../mixins/orderStorageMixin';
|
||||
import orderValidationMixin from '../../mixins/orderValidationMixin';
|
||||
import { useStore } from '../../store/store';
|
||||
import { currentFormattedHours, currentFormattedMinutes } from '../../utils/dateUtils';
|
||||
import { LucideCopy, LucidePencil, LucideRotateCcw, LucideSave } from 'lucide-vue-next';
|
||||
import { setOrderToDefault } from '../../utils/orderUtils';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'OrderMessage',
|
||||
components: { LucideCopy, LucidePencil, LucideRotateCcw, LucideSave },
|
||||
|
||||
mixins: [orderStorageMixin, orderValidationMixin],
|
||||
|
||||
data() {
|
||||
return {
|
||||
actionMonit: '',
|
||||
monitTimeout: null as number | null,
|
||||
|
||||
incrementOnSave: true,
|
||||
incrementOnCopy: true,
|
||||
updateDate: true
|
||||
};
|
||||
},
|
||||
|
||||
setup() {
|
||||
return {
|
||||
store: useStore()
|
||||
};
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.incrementOnSave = this.getOrderSetting('save-increment') !== 'false';
|
||||
this.incrementOnCopy = this.getOrderSetting('copy-increment') !== 'false';
|
||||
this.updateDate = this.getOrderSetting('update-date') !== 'false';
|
||||
},
|
||||
|
||||
computed: {
|
||||
fullOrderMessage() {
|
||||
return this.store.orderMessage + this.store.footerMessage;
|
||||
},
|
||||
|
||||
// Replace all new line tags with <br> for preview and get rid of the first one (visible only on simulator's chat)
|
||||
orderMessagePreview() {
|
||||
return this.fullOrderMessage.replace(/\n/g, '<br>').replace('<br>', '');
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
fullOrderMessage() {
|
||||
if (this.updateDate) {
|
||||
this.store.orderFooter['hour'] = currentFormattedHours();
|
||||
this.store.orderFooter['minutes'] = currentFormattedMinutes();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
onCheckboxChange(e: Event) {
|
||||
const checkbox = e.target as HTMLInputElement;
|
||||
this.saveOrderSetting(checkbox.id, checkbox.checked);
|
||||
},
|
||||
|
||||
showActionMonit(text: string) {
|
||||
if (this.monitTimeout != null) {
|
||||
this.actionMonit = '';
|
||||
clearTimeout(this.monitTimeout);
|
||||
|
||||
setTimeout(() => {
|
||||
this.actionMonit = text;
|
||||
}, 100);
|
||||
} else {
|
||||
this.actionMonit = text;
|
||||
}
|
||||
|
||||
this.monitTimeout = window.setTimeout(() => {
|
||||
this.actionMonit = '';
|
||||
this.monitTimeout = null;
|
||||
}, 5000);
|
||||
},
|
||||
|
||||
incrementOrderNo() {
|
||||
const order = this.store[this.store.chosenOrderType];
|
||||
|
||||
order.header.orderNo = (Number(order.header.orderNo) + 1).toString();
|
||||
},
|
||||
|
||||
copyMessage() {
|
||||
if (!navigator.clipboard)
|
||||
return this.showActionMonit(this.$t('order-message.warning-outdated-clipboard'));
|
||||
|
||||
const hasAtLeastOneRow = /(\[ \d \])/g.test(this.fullOrderMessage);
|
||||
const hasAllInputsFilled = !/_/g.test(this.store.orderMessage);
|
||||
|
||||
if (!hasAllInputsFilled)
|
||||
return this.showActionMonit(
|
||||
`<span class="text--warn">${this.$t('order-message.warning-fill-inputs')}</span>`
|
||||
);
|
||||
if (!hasAtLeastOneRow)
|
||||
return this.showActionMonit(
|
||||
`<span class="text--warn">${this.$t('order-message.warning-add-rows')}</span>`
|
||||
);
|
||||
|
||||
const fieldsToCorrect = this.verifyOrderFields();
|
||||
|
||||
if (fieldsToCorrect.length > 0)
|
||||
return this.showActionMonit(
|
||||
`<span class="text--warn">${this.$t('order-message.warning-fill-footer')} ${fieldsToCorrect.join(
|
||||
', '
|
||||
)}</span>`
|
||||
);
|
||||
|
||||
navigator.clipboard.writeText(this.fullOrderMessage);
|
||||
|
||||
if (this.incrementOnCopy) this.incrementOrderNo();
|
||||
|
||||
this.showActionMonit(this.$t('order-message.success-copy-html'));
|
||||
},
|
||||
|
||||
saveOrder() {
|
||||
const savedOrderStatus = this.saveLocalOrder();
|
||||
|
||||
switch (savedOrderStatus) {
|
||||
case -1:
|
||||
this.showActionMonit(
|
||||
`<span class="text--warn">${this.$t('order-message.warning-fill-top')}</span>`
|
||||
);
|
||||
break;
|
||||
case 0:
|
||||
this.showActionMonit(
|
||||
`<span class="text--warn">${this.$t('order-message.warning-order-identical')}</span>`
|
||||
);
|
||||
break;
|
||||
case 1:
|
||||
this.showActionMonit(this.$t('order-message.success-save-html'));
|
||||
|
||||
if (this.incrementOnSave) this.incrementOrderNo();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
updateOrder() {
|
||||
const updatedOrderStatus = this.updateLocalOrder();
|
||||
|
||||
switch (updatedOrderStatus) {
|
||||
case -1:
|
||||
this.showActionMonit(
|
||||
`<span class="text--warn">${this.$t('order-message.error-update')}</span>`
|
||||
);
|
||||
break;
|
||||
|
||||
case 0:
|
||||
this.showActionMonit(
|
||||
`<span class="text--warn">${this.$t('order-message.warning-no-order-selected')}</span>`
|
||||
);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
this.showActionMonit(this.$t('order-message.success-update-html'));
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
resetOrder() {
|
||||
setOrderToDefault(this.store[this.store.chosenOrderType]);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@use '../../styles/colors';
|
||||
|
||||
.order-message {
|
||||
overflow: auto;
|
||||
|
||||
h3 {
|
||||
margin: 0;
|
||||
margin-bottom: 1em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
button {
|
||||
margin: 0 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
.message_body {
|
||||
height: 350px;
|
||||
|
||||
background-color: colors.$bgColLighter;
|
||||
color: white;
|
||||
text-align: justify;
|
||||
|
||||
border-radius: 0.5em;
|
||||
padding: 0.5em;
|
||||
user-select: none;
|
||||
-moz-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
.message_info {
|
||||
text-align: center;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.message_actions {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 0.5em;
|
||||
|
||||
button.icon {
|
||||
gap: 0.5em;
|
||||
}
|
||||
|
||||
button img {
|
||||
height: 2ch;
|
||||
vertical-align: text-bottom;
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
|
||||
button[data-disabled='true'] {
|
||||
user-select: none;
|
||||
color: #aaa;
|
||||
}
|
||||
}
|
||||
|
||||
.message_checkboxes {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
.action_monit {
|
||||
text-align: center;
|
||||
padding: 1.5em;
|
||||
font-size: 1.15em;
|
||||
}
|
||||
|
||||
.monit-anim {
|
||||
&-enter-active,
|
||||
&-leave-active {
|
||||
transition: all 100ms ease-in-out;
|
||||
}
|
||||
|
||||
&-enter-from,
|
||||
&-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateY(-20px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,371 @@
|
||||
<template>
|
||||
<div class="order-train-picker">
|
||||
<div class="options">
|
||||
<div class="options-top">
|
||||
<select
|
||||
name="dispatcher-select"
|
||||
id="dispatcher-select"
|
||||
v-model="selectedSceneryId"
|
||||
@change="selectOption"
|
||||
>
|
||||
<option :value="null" disabled>
|
||||
{{ $t('order-train-picker.placeholder-scenery-name') }}
|
||||
</option>
|
||||
<option
|
||||
v-for="scenery in filteredSceneries"
|
||||
:value="`${scenery.stationName}|${scenery.stationHash}|${scenery.dispatcherName}|${scenery.region}`"
|
||||
:key="scenery.dispatcherName + scenery.stationName"
|
||||
>
|
||||
{{ scenery.stationName }} • {{ scenery.dispatcherName }}
|
||||
</option>
|
||||
</select>
|
||||
|
||||
<select
|
||||
name="region-select"
|
||||
id="region-select"
|
||||
v-model="selectedRegion"
|
||||
@change="selectOption"
|
||||
>
|
||||
<option :value="null" disabled>
|
||||
{{ $t('order-train-picker.placeholder-region-name') }}
|
||||
</option>
|
||||
<option v-for="region in regions" :value="region" :key="region">
|
||||
{{ getRegionNameById(region) }}
|
||||
</option>
|
||||
<!-- <option
|
||||
v-for="scenery in filteredSceneries"
|
||||
:value="`${scenery.stationName}|${scenery.stationHash}|${scenery.dispatcherName}|${scenery.region}`"
|
||||
:key="scenery.dispatcherName + scenery.stationName"
|
||||
>
|
||||
|
||||
</option> -->
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<select
|
||||
name="checkpoint-select"
|
||||
id="checkpoint-select"
|
||||
v-model="selectedCheckpointName"
|
||||
:disabled="!selectedScenery"
|
||||
>
|
||||
<option :value="null" disabled>
|
||||
{{ $t('order-train-picker.placeholder-checkpoint-name') }}
|
||||
</option>
|
||||
<option :value="cp" v-for="cp in checkpointNameList" :key="cp">
|
||||
{{ cp }}
|
||||
</option>
|
||||
</select>
|
||||
|
||||
<label for="fill-checkpoint" class="g-checkbox">
|
||||
<input
|
||||
type="checkbox"
|
||||
name="fill-checkpoint"
|
||||
id="fill-checkpoint"
|
||||
v-model="fillCheckpointName"
|
||||
/>
|
||||
<span> {{ $t('order-train-picker.autofill-checkpoint-id') }}</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<b v-if="!selectedSceneryId" class="text--accent">
|
||||
{{ $t('order-train-picker.info') }}
|
||||
</b>
|
||||
|
||||
<div v-else>
|
||||
<div style="margin-bottom: 0.5em">
|
||||
<h3 style="margin-bottom: 0.5em">{{ $t('order-train-picker.title') }}</h3>
|
||||
<b class="text--accent">{{ $t('order-train-picker.subtitle') }}</b>
|
||||
</div>
|
||||
|
||||
<ul class="train-list">
|
||||
<li
|
||||
v-for="train in sceneryTrains"
|
||||
:key="train.trainNo + train.driverName"
|
||||
@click="fillOrder(train.trainNo)"
|
||||
>
|
||||
<button class="g-button">
|
||||
<span
|
||||
v-if="train.currentStationName == selectedScenery?.stationName"
|
||||
class="online-indicator"
|
||||
></span>
|
||||
|
||||
<span>
|
||||
{{ train.driverName }} •
|
||||
<span v-if="train.timetable" style="color: gold">{{
|
||||
train.timetable.category
|
||||
}}</span>
|
||||
{{ train.trainNo }}
|
||||
</span>
|
||||
</button>
|
||||
</li>
|
||||
|
||||
<li class="no-trains" v-if="sceneryTrains?.length == 0 && selectedSceneryId">
|
||||
{{ $t('order-train-picker.no-trains') }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import http from '../../http';
|
||||
import { useStore } from '../../store/store';
|
||||
import { API } from '../../types/apiTypes';
|
||||
import { ISceneryData } from '../../types/dataTypes';
|
||||
import {
|
||||
currentFormattedDate,
|
||||
currentFormattedHours,
|
||||
currentFormattedMinutes
|
||||
} from '../../utils/dateUtils';
|
||||
import { getRegionNameById } from '../../utils/sceneryUtils';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'order-train-picker',
|
||||
|
||||
data() {
|
||||
return {
|
||||
sceneriesData: undefined as ISceneryData[] | undefined,
|
||||
activeData: undefined as API.ActiveData.Response | undefined,
|
||||
|
||||
selectedSceneryId: null as string | null,
|
||||
selectedCheckpointName: null as string | null,
|
||||
selectedRegion: 'eu',
|
||||
|
||||
fillCheckpointName: false,
|
||||
|
||||
refreshInterval: -1,
|
||||
store: useStore(),
|
||||
|
||||
regions: ['eu', 'cae', 'usw', 'us', 'ru']
|
||||
};
|
||||
},
|
||||
|
||||
created() {
|
||||
this.fillCheckpointName = window.localStorage.getItem('fill-checkpoint') !== 'false';
|
||||
|
||||
this.fetchSceneriesData();
|
||||
},
|
||||
|
||||
async activated() {
|
||||
await this.fetchActiveData();
|
||||
this.handleQueries();
|
||||
|
||||
this.refreshInterval = window.setInterval(() => {
|
||||
this.fetchActiveData();
|
||||
}, 25 * 1000);
|
||||
},
|
||||
|
||||
deactivated() {
|
||||
window.clearInterval(this.refreshInterval);
|
||||
},
|
||||
|
||||
watch: {
|
||||
fillCheckpointName(val: boolean) {
|
||||
window.localStorage.setItem('fill-checkpoint', `${val}`);
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
selectedScenery() {
|
||||
return this.activeData?.activeSceneries?.find(
|
||||
(scenery) =>
|
||||
this.selectedSceneryId ==
|
||||
`${scenery.stationName}|${scenery.stationHash}|${scenery.dispatcherName}|${scenery.region}` &&
|
||||
this.selectedRegion == scenery.region
|
||||
);
|
||||
},
|
||||
|
||||
filteredSceneries() {
|
||||
return this.activeData?.activeSceneries
|
||||
?.filter((s) => s.isOnline && s.region == this.selectedRegion)
|
||||
.sort((s1, s2) => s1.stationName.localeCompare(s2.stationName));
|
||||
},
|
||||
|
||||
checkpointNameList() {
|
||||
if (!this.selectedScenery) return [];
|
||||
|
||||
const checkpoints =
|
||||
this.sceneriesData?.find((s) => s.name == this.selectedScenery?.stationName)?.checkpoints ??
|
||||
'';
|
||||
|
||||
if (checkpoints.length == 0) return [this.selectedScenery.stationName];
|
||||
|
||||
return checkpoints.split(';');
|
||||
},
|
||||
|
||||
sceneryTrains() {
|
||||
if (!this.selectedScenery || !this.activeData?.trains) return [];
|
||||
|
||||
const scenery = this.selectedScenery;
|
||||
|
||||
return this.activeData.trains
|
||||
?.filter(
|
||||
(t) =>
|
||||
(t.currentStationName == scenery.stationName &&
|
||||
t.region == scenery.region &&
|
||||
(t.online || t.lastSeen >= Date.now() - 60000)) ||
|
||||
t.timetable?.path.includes(`${scenery.stationName} ${scenery.stationHash}.sc`)
|
||||
)
|
||||
.sort((t1, t2) => {
|
||||
return (
|
||||
(t2.currentStationName == scenery.stationName ? 1 : -1) -
|
||||
(t1.currentStationName == scenery.stationName ? 1 : -1) ||
|
||||
t1.driverName.localeCompare(t2.driverName)
|
||||
);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
getRegionNameById,
|
||||
|
||||
async fetchSceneriesData() {
|
||||
const data: ISceneryData[] = (await http.get<ISceneryData[]>('api/getSceneries')).data;
|
||||
|
||||
this.sceneriesData = data;
|
||||
},
|
||||
|
||||
async fetchActiveData() {
|
||||
const data: API.ActiveData.Response = await (await http.get('api/getActiveData')).data;
|
||||
|
||||
this.activeData = data;
|
||||
},
|
||||
|
||||
selectOption() {
|
||||
this.selectedCheckpointName =
|
||||
this.checkpointNameList.length == 0 ? null : this.checkpointNameList[0];
|
||||
},
|
||||
|
||||
fillOrder(trainNo: number) {
|
||||
if (!this.selectedScenery) return;
|
||||
|
||||
const chosenOrder = this.store[this.store.chosenOrderType];
|
||||
chosenOrder.header.trainNo = trainNo.toString();
|
||||
chosenOrder.header.date = currentFormattedDate();
|
||||
|
||||
this.store.orderFooter.dispatcherName = this.selectedScenery.dispatcherName;
|
||||
this.store.orderFooter.stationName =
|
||||
this.selectedCheckpointName?.split(',')[0] || this.selectedScenery.stationName;
|
||||
this.store.orderFooter.hour = currentFormattedHours();
|
||||
this.store.orderFooter.minutes = currentFormattedMinutes();
|
||||
|
||||
if (this.fillCheckpointName) {
|
||||
const sceneryAbbrev = this.sceneriesData?.find(
|
||||
({ name }) => name === this.selectedScenery!.stationName
|
||||
)?.abbr;
|
||||
|
||||
this.store.orderFooter.checkpointName =
|
||||
sceneryAbbrev || this.store.orderFooter.stationName.slice(0, 2);
|
||||
}
|
||||
|
||||
this.store.panelMode = 'OrderMessagePanel';
|
||||
},
|
||||
|
||||
handleQueries() {
|
||||
const query = new URLSearchParams(window.location.search);
|
||||
|
||||
const id = query.get('sceneryId');
|
||||
|
||||
if (id) {
|
||||
const [sceneryName, sceneryRegion] = id.split('|');
|
||||
|
||||
this.selectedRegion = sceneryRegion;
|
||||
|
||||
const queryScenery = this.activeData?.activeSceneries?.find(
|
||||
(sc) => sc.stationName == sceneryName && sc.region == sceneryRegion && sc.isOnline
|
||||
);
|
||||
|
||||
if (queryScenery) {
|
||||
this.selectedSceneryId = `${queryScenery.stationName}|${queryScenery.stationHash}|${queryScenery.dispatcherName}|${queryScenery.region}`;
|
||||
|
||||
this.selectOption();
|
||||
|
||||
this.store.panelMode = 'OrderTrainPickerPanel';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@use '../../styles/colors';
|
||||
|
||||
.order-train-picker {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
overflow: auto;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
.options {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
width: 100%;
|
||||
gap: 0.5em;
|
||||
|
||||
select {
|
||||
background-color: colors.$bgColDarker;
|
||||
|
||||
font-size: 1em;
|
||||
width: 100%;
|
||||
|
||||
&[disabled] {
|
||||
color: gray;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.options-top {
|
||||
display: grid;
|
||||
grid-template-columns: 3fr auto;
|
||||
gap: 0.5em;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.content {
|
||||
margin-top: 1em;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
ul.train-list {
|
||||
padding: 1px;
|
||||
|
||||
li.no-trains {
|
||||
font-weight: bold;
|
||||
background-color: colors.$bgColDarker;
|
||||
padding: 0.5em;
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
|
||||
li > button {
|
||||
width: 100%;
|
||||
background-color: colors.$bgColDarker;
|
||||
padding: 0.5em;
|
||||
margin-top: 0.5em;
|
||||
|
||||
&:hover {
|
||||
background-color: colors.$bgColLighter;
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
outline: 1px solid colors.$accentCol;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.online-indicator {
|
||||
display: inline-block;
|
||||
width: 9px;
|
||||
height: 9px;
|
||||
vertical-align: middle;
|
||||
background-color: greenyellow;
|
||||
border-radius: 100%;
|
||||
margin: 0 5px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"orderS": [
|
||||
"D"
|
||||
]
|
||||
}
|
||||
@@ -13,8 +13,10 @@ export const handleOrderPlaceholders = (isRowEnabled: boolean, rowRef: HTMLTable
|
||||
if (!node.getAttribute('holder')) return;
|
||||
const radioCheckedAttr = node.getAttribute('radio-checked');
|
||||
|
||||
if (radioCheckedAttr == null) return node.setAttribute('placeholder', node.getAttribute('holder')!);
|
||||
if (radioCheckedAttr == 'true') return node.setAttribute('placeholder', node.getAttribute('holder')!);
|
||||
if (radioCheckedAttr == null)
|
||||
return node.setAttribute('placeholder', node.getAttribute('holder')!);
|
||||
if (radioCheckedAttr == 'true')
|
||||
return node.setAttribute('placeholder', node.getAttribute('holder')!);
|
||||
if (node.getAttribute('placeholder') == null) return;
|
||||
|
||||
node.setAttribute('holder', node.getAttribute('placeholder')!);
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import axios from 'axios';
|
||||
|
||||
const http = axios.create({
|
||||
baseURL: 'https://stacjownik.spythere.eu'
|
||||
});
|
||||
|
||||
export default http;
|
||||
@@ -0,0 +1,19 @@
|
||||
import enLang from './locales/en.json';
|
||||
import plLang from './locales/pl.json';
|
||||
|
||||
import { createI18n } from 'vue-i18n';
|
||||
|
||||
const i18n = createI18n({
|
||||
locale: 'pl',
|
||||
legacy: false,
|
||||
warnHtmlMessage: false,
|
||||
fallbackLocale: 'pl',
|
||||
|
||||
messages: {
|
||||
en: enLang,
|
||||
pl: plLang
|
||||
},
|
||||
enableLegacy: false
|
||||
});
|
||||
|
||||
export default i18n;
|
||||
@@ -0,0 +1,78 @@
|
||||
{
|
||||
"locale": {
|
||||
"pl": "POL",
|
||||
"en": "ENG"
|
||||
},
|
||||
"navbar": {
|
||||
"OrderMessagePanel": "MESSAGE",
|
||||
"OrderListPanel": "SAVED",
|
||||
"OrderTrainPickerPanel": "TRAINS"
|
||||
},
|
||||
"update": {
|
||||
"update-available-text": "New GeneraTOR version is available!",
|
||||
"update-available-underline": "Click here to update!",
|
||||
"title": "GeneraTOR update!",
|
||||
"confirm": "ROGER THAT!",
|
||||
"no-data": "No changelog available!",
|
||||
"info-1": "This changelog will be available to see once again after clicking the version number in the footer",
|
||||
"info-2": "The full app changelog available on <a href='https://github.com/Spythere/genera-tor' target='_blank'>the project's GitHub</a>"
|
||||
},
|
||||
"order-message": {
|
||||
"title": "Message to display in the simulator's chatbox:",
|
||||
"info": "Copy or save the content of the generated train order using buttons below:",
|
||||
"button-save": "Save as new",
|
||||
"button-copy": "Copy the message",
|
||||
"button-update": "Update",
|
||||
"button-reset": "Reset",
|
||||
"warning-outdated-clipboard": "Oops! Your browser may be a little bit depraceted since it's not supporting saving data to the clipboard! :/",
|
||||
"warning-fill-inputs": "Fill all the empty fields before copying the order!",
|
||||
"warning-add-rows": "Add at least one row before copying the order!",
|
||||
"warning-fill-footer": "Fill the following rows in the order's footer before copying it:",
|
||||
"warning-fill-top": "Fill the order number, train number and date before saving it!",
|
||||
"warning-order-identical": "Last saved order is identical as the current one!",
|
||||
"warning-no-order-selected": "Choose the already saved order first!",
|
||||
"error-update": "An error occurred while saving this order! :/",
|
||||
"success-update-html": "Updated this <b class=\"text--accent\">order's</b> message!",
|
||||
"success-save-html": "Saved <b class=\"text--accent\">order's</b> message in the browser memory!",
|
||||
"success-copy-html": "<b class=\"text--accent\">Success!</b> You may paste the order message in the simulator's chatbox now!"
|
||||
},
|
||||
"order-footer": {
|
||||
"field-stationName": "station",
|
||||
"field-checkpointName": "checkpoint",
|
||||
"field-hour": "hour",
|
||||
"field-minutes": "minute",
|
||||
"field-dispatcherName": "dispatcher",
|
||||
"field-secondaryDispatcherName": "ordering dispatcher",
|
||||
"field-dispatcherOrSecondaryName": "dispatcher (or ordering dispatcher)"
|
||||
},
|
||||
"order-options": {
|
||||
"dark-mode": "Order dark theme",
|
||||
"update-number-on-copy": "Update order number on copy",
|
||||
"update-number-on-save": "Update order number on save",
|
||||
"update-hours": "Update order hour on edit"
|
||||
},
|
||||
"order-list": {
|
||||
"title": "Saved train orders",
|
||||
"order-title": "Order \"{orderName}\" no. {orderNo} for train no. {trainNo}",
|
||||
"no-saved-orders": "No saved orders!",
|
||||
"order-added": "Added:",
|
||||
"order-updated": "Updated:",
|
||||
"button-order-select": "Select",
|
||||
"button-order-remove": "Remove"
|
||||
},
|
||||
"order-train-picker": {
|
||||
"placeholder-scenery-name": "Scenery name",
|
||||
"placeholder-region-name": "Region",
|
||||
"placeholder-checkpoint-name": "Checkpoint name",
|
||||
"autofill-checkpoint-id": "Autofill checkpoint's abbreviation",
|
||||
"info": "Select scenery name to display active trains",
|
||||
"title": "Active timetables and trains on the scenery",
|
||||
"subtitle": "Click on the user below to fill the current order with their information",
|
||||
"no-trains": "No trains to display"
|
||||
},
|
||||
"migrate-info": {
|
||||
"line-1": "GeneraTOR is being moved to a new domain - {0}! You can still use the current website, but it will no longer be updated and will be shut down in the nearest future!",
|
||||
"link": "https://generator-td2.spythere.eu/",
|
||||
"accept-btn": "ROGER THAT!"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
{
|
||||
"locale": {
|
||||
"pl": "POL",
|
||||
"en": "ENG"
|
||||
},
|
||||
"update": {
|
||||
"update-available-text": "Nowa wersja GeneraTORa dostępna!",
|
||||
"update-available-underline": "Kliknij, aby odświeżyć aplikację!",
|
||||
"title": "Aktualizacja GeneraTORa!",
|
||||
"no-data": "Brak dostępnego changelogu!",
|
||||
"confirm": "Przyjąłem!",
|
||||
"info-1": "Ten changelog będzie zawsze dostępny po kliknięciu numeru wersji w stopce strony",
|
||||
"info-2": "Pełny changelog dostępny na <a href='https://github.com/Spythere/genera-tor' target='_blank'>GitHubie projektu</a>"
|
||||
},
|
||||
"navbar": {
|
||||
"OrderMessagePanel": "WIADOMOŚĆ",
|
||||
"OrderListPanel": "ZAPISANE",
|
||||
"OrderTrainPickerPanel": "POCIĄGI"
|
||||
},
|
||||
"order-message": {
|
||||
"title": "Wiadomość do wyświetlenia na czacie symulatora:",
|
||||
"info": "Po wygenerowaniu rozkazu skopiuj jego treść lub zapisz w pamięci przeglądarki za pomocą przycisków poniżej:",
|
||||
"button-save": "Zapisz nowy",
|
||||
"button-copy": "Skopiuj treść",
|
||||
"button-update": "Zaktualizuj",
|
||||
"button-reset": "Zresetuj",
|
||||
"warning-outdated-clipboard": "Ups! Twoja przeglądarka musi być dosyć przestarzała, ponieważ nie obsługuje zapisu do schowka! :/",
|
||||
"warning-fill-inputs": "Wypełnij puste rubryki rozkazu przed jego skopiowaniem!",
|
||||
"warning-add-rows": "Dodaj co najmniej jedną działkę rozkazu przed jego skopiowaniem!",
|
||||
"warning-fill-footer": "Uzupełnij następujące rubryki na dole rozkazu przed jego skopiowaniem:",
|
||||
"warning-fill-top": "Wypełnij numer rozkazu, numer pociągu i datę zanim dodasz rozkaz!",
|
||||
"warning-order-identical": "Ostatni zapisany rozkaz jest identyczny z obecnym!",
|
||||
"warning-no-order-selected": "Wybierz rozkaz, który chcesz zaktualizować!",
|
||||
"error-update": "Wystąpił błąd podczas aktualizowania tego rozkazu! :/",
|
||||
"success-update-html": "Zaktualizowano treść <b class=\"text--accent\">rozkazu</b>!",
|
||||
"success-save-html": "Zapisano treść <b class=\"text--accent\">rozkazu</b> w pamięci przeglądarki!",
|
||||
"success-copy-html": "<b class=\"text--accent\">Skopiowano!</b> Możesz teraz wkleić treść rozkazu na czacie symulatora!"
|
||||
},
|
||||
"order-footer": {
|
||||
"field-stationName": "stacja",
|
||||
"field-checkpointName": "posterunek",
|
||||
"field-hour": "godzina",
|
||||
"field-minutes": "minuta",
|
||||
"field-dispatcherName": "dyżurny ruchu",
|
||||
"field-secondaryDispatcherName": "z polecenia dyżurnego ruchu",
|
||||
"field-dispatcherOrSecondaryName": "dyżurny ruchu (lub z polecenia dyżurnego ruchu)"
|
||||
},
|
||||
"order-options": {
|
||||
"dark-mode": "Ciemny motyw bloczka rozkazu",
|
||||
"update-number-on-copy": "Aktualizuj numer rozkazu po skopiowaniu",
|
||||
"update-number-on-save": "Aktualizuj numer rozkazu po zapisaniu",
|
||||
"update-hours": "Aktualizuj godziny przy edycji"
|
||||
},
|
||||
"order-list": {
|
||||
"title": "Zapisane rozkazy pisemne",
|
||||
"no-saved-orders": "Brak zapisanych rozkazów!",
|
||||
"order-title": "Rozkaz \"{orderName}\" nr {orderNo} dla pociągu nr {trainNo}",
|
||||
"order-added": "Dodano:",
|
||||
"order-updated": "Zaktualizowano:",
|
||||
"button-order-select": "Wybierz",
|
||||
"button-order-remove": "Usuń"
|
||||
},
|
||||
"order-train-picker": {
|
||||
"placeholder-scenery-name": "Sceneria",
|
||||
"placeholder-region-name": "Region",
|
||||
"placeholder-checkpoint-name": "Posterunek",
|
||||
"autofill-checkpoint-id": "Uzupełniaj skrót wybranego posterunku",
|
||||
"info": "Wybierz dyżurnego oraz scenerię, aby zobaczyć pociągi",
|
||||
"title": "Aktywne RJ i gracze na scenerii",
|
||||
"subtitle": "Kliknij na gracza, aby wypełnić obecny rozkaz jego danymi",
|
||||
"no-trains": "Brak pociągów do wyświetlenia"
|
||||
},
|
||||
"migrate-info": {
|
||||
"line-1": "GeneraTOR zostaje przeniesiony na nową domenę - {0}! Możesz korzystać z obecnej strony, jednak nie będzie ona otrzymywać już aktualizacji i w przyszłości zostanie wyłączona!",
|
||||
"link": "https://generator-td2.spythere.eu/",
|
||||
"accept-btn": "PRZYJĄŁEM!"
|
||||
}
|
||||
}
|
||||
@@ -3,5 +3,6 @@ import App from './App.vue';
|
||||
import router from './router';
|
||||
import { createPinia } from 'pinia';
|
||||
|
||||
createApp(App).use(router).use(createPinia()).mount('#app');
|
||||
import i18n from './i18n';
|
||||
|
||||
createApp(App).use(router).use(i18n).use(createPinia()).mount('#app');
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
export default class StorageManager {
|
||||
static registerStorage(name: string) {
|
||||
window.localStorage.setItem(name, '1');
|
||||
}
|
||||
|
||||
static unregisterStorage(name: string) {
|
||||
window.localStorage.removeItem(name);
|
||||
}
|
||||
|
||||
static isRegistered(name: string) {
|
||||
return window.localStorage.getItem(name) ? true : false;
|
||||
}
|
||||
|
||||
static setBooleanValue(key: string, val: boolean) {
|
||||
window.localStorage.setItem(key, val.toString());
|
||||
}
|
||||
|
||||
static setNumericValue(key: string, val: number) {
|
||||
window.localStorage.setItem(key, val.toString());
|
||||
}
|
||||
|
||||
static setStringValue(key: string, val: string) {
|
||||
window.localStorage.setItem(key, val);
|
||||
}
|
||||
|
||||
static setValue(key: string, val: any) {
|
||||
if (typeof val == 'boolean') this.setBooleanValue(key, val);
|
||||
else if (typeof val == 'number') this.setNumericValue(key, val);
|
||||
else if (typeof val == 'string') this.setStringValue(key, val);
|
||||
else this.setStringValue(key, val);
|
||||
}
|
||||
|
||||
static removeValue(key: string) {
|
||||
window.localStorage.removeItem(key);
|
||||
}
|
||||
|
||||
static getValue(key: string) {
|
||||
return window.localStorage.getItem(key);
|
||||
}
|
||||
|
||||
static getBooleanValue(key: string): boolean {
|
||||
return window.localStorage.getItem(key) === 'true' ? true : false;
|
||||
}
|
||||
|
||||
static getStringValue(key: string): string {
|
||||
return window.localStorage.getItem(key) || '';
|
||||
}
|
||||
|
||||
static getNumericValue(key: string): number {
|
||||
const itemValue = window.localStorage.getItem(key);
|
||||
return itemValue ? parseInt(itemValue) : 0;
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import { useStore } from '../store/store';
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
return {
|
||||
store: useStore(),
|
||||
store: useStore()
|
||||
};
|
||||
},
|
||||
|
||||
@@ -14,7 +14,7 @@ export default defineComponent({
|
||||
|
||||
const messageArray = [];
|
||||
|
||||
if (footer.stationName) messageArray.push(`stacja: ${footer.stationName}`);
|
||||
messageArray.push(`stacja: ${footer.stationName ?? ''}`);
|
||||
if (footer.checkpointName) messageArray.push(`posterunek: ${footer.checkpointName}`);
|
||||
if (footer.hour) messageArray.push(`godz. ${footer.hour}`);
|
||||
if (footer.minutes) messageArray.push(`min. ${footer.minutes}`);
|
||||
@@ -22,10 +22,9 @@ export default defineComponent({
|
||||
if (footer.secondaryDispatcherName)
|
||||
messageArray.push(`z polecenia dyżurnego ruchu ${footer.secondaryDispatcherName}`);
|
||||
|
||||
this.store.footerMessage = ` <b>|</b> ${messageArray.join(
|
||||
this.store.footerMessage = `\n--------\n${messageArray.join(
|
||||
', '
|
||||
)} <b>|</b> Rozkaz otrzymałem, maszynista: (potwierdzić otrzymanie rozkazu)`;
|
||||
},
|
||||
},
|
||||
)}\n--------\nRozkaz otrzymałem, maszynista: <i>(potwierdzić otrzymanie rozkazu)</i>`;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -2,21 +2,39 @@ import { defineComponent } from 'vue';
|
||||
import { useStore } from '../store/store';
|
||||
import { LocalStorageOrder } from '../types/orderTypes';
|
||||
|
||||
function alertWrongOrderFormat() {
|
||||
alert('Wystąpił błąd podczas przetwarzania rozkazu! Informacje mogą być niepoprawne!');
|
||||
console.warn('Zły format zapisanego rozkazu!');
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
return {
|
||||
store: useStore(),
|
||||
store: useStore()
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
saveOrderSetting(key: string, value: string | number | boolean) {
|
||||
window.localStorage.setItem(key, value.toString());
|
||||
},
|
||||
|
||||
getOrderSetting(key: string) {
|
||||
return window.localStorage.getItem(key);
|
||||
},
|
||||
|
||||
removeOrderSetting(key: string) {
|
||||
window.localStorage.removeItem(key);
|
||||
},
|
||||
|
||||
saveLocalOrder() {
|
||||
let orderObj: LocalStorageOrder = {
|
||||
const orderObj: LocalStorageOrder = {
|
||||
id: '',
|
||||
orderType: this.store.chosenOrderType,
|
||||
orderBody: this.store[this.store.chosenOrderType],
|
||||
orderFooter: this.store.orderFooter,
|
||||
createdAt: Date.now(),
|
||||
orderVersion: import.meta.env['VITE_APP_ORDER_VERSION'] || '1'
|
||||
};
|
||||
|
||||
const headerInfo = orderObj['orderBody']['header'];
|
||||
@@ -52,12 +70,13 @@ export default defineComponent({
|
||||
|
||||
if (!localOrder) return -1;
|
||||
|
||||
let orderObj: LocalStorageOrder = {
|
||||
const orderObj: LocalStorageOrder = {
|
||||
id: this.store.chosenLocalOrderId,
|
||||
orderType: this.store.chosenOrderType,
|
||||
orderBody: this.store[this.store.chosenOrderType],
|
||||
orderFooter: this.store.orderFooter,
|
||||
updatedAt: Date.now(),
|
||||
orderVersion: import.meta.env['VITE_APP_ORDER_VERSION'] || '1'
|
||||
};
|
||||
|
||||
window.localStorage.setItem(this.store.chosenLocalOrderId, JSON.stringify(orderObj));
|
||||
@@ -72,51 +91,73 @@ export default defineComponent({
|
||||
// localStorage.setItem('orderCount', (Number(localStorage.getItem('orderCount')) - 1).toString());
|
||||
},
|
||||
|
||||
selectLocalOrder(order: LocalStorageOrder) {
|
||||
this.store.chosenOrderType = order.orderType;
|
||||
this.store.chosenLocalOrderId = order.id;
|
||||
selectLocalOrder(localOrder: LocalStorageOrder) {
|
||||
// const localOrder = JSON.parse(JSON.stringify(order));
|
||||
const { orderBody: localOrderBody, orderFooter: localOrderFooter } = localOrder;
|
||||
|
||||
const localOrder = JSON.parse(JSON.stringify(order));
|
||||
const localOrderBody = localOrder['orderBody'];
|
||||
const localOrderFooter = localOrder['orderFooter'];
|
||||
this.store[localOrder.orderType].header.date = localOrderBody.header.date;
|
||||
this.store[localOrder.orderType].header.orderNo = localOrderBody.header.orderNo;
|
||||
this.store[localOrder.orderType].header.trainNo = localOrderBody.header.trainNo;
|
||||
|
||||
let storeOrderObj;
|
||||
if (localOrder.orderType == 'orderN' || localOrder.orderType == 'orderS') {
|
||||
const currentOrder = this.store[localOrder.orderType];
|
||||
|
||||
switch (order.orderType) {
|
||||
case 'orderN':
|
||||
case 'orderS':
|
||||
storeOrderObj = this.store[order.orderType];
|
||||
for (let orderKey in storeOrderObj) {
|
||||
for (let propKey in (storeOrderObj as any)[orderKey]) {
|
||||
(storeOrderObj as any)[orderKey][propKey] = localOrderBody[orderKey][propKey];
|
||||
}
|
||||
if (localOrderBody.rows.length != currentOrder.rows.length) {
|
||||
alertWrongOrderFormat();
|
||||
return;
|
||||
}
|
||||
|
||||
for (let rowIndex = 0; rowIndex < currentOrder.rows.length; rowIndex++) {
|
||||
const row = currentOrder.rows[rowIndex];
|
||||
|
||||
if (localOrderBody.rows[rowIndex] === undefined) {
|
||||
alertWrongOrderFormat();
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
case 'orderO':
|
||||
storeOrderObj = this.store[order.orderType];
|
||||
|
||||
storeOrderObj['other'] = localOrderBody['other'];
|
||||
storeOrderObj['header']['date'] = localOrderBody['header']['date'];
|
||||
storeOrderObj['header']['orderNo'] = localOrderBody['header']['orderNo'];
|
||||
storeOrderObj['header']['trainNo'] = localOrderBody['header']['trainNo'];
|
||||
|
||||
for (let i = 0; i < storeOrderObj['orderList'].length; i++) {
|
||||
const orderItem = storeOrderObj['orderList'][i];
|
||||
|
||||
for (let prop in orderItem) {
|
||||
(storeOrderObj['orderList'][i] as any)[prop] = localOrderBody['orderList'][i][prop];
|
||||
for (const rowProp in row) {
|
||||
if (localOrderBody.rows[rowIndex][rowProp] === undefined) {
|
||||
alertWrongOrderFormat();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
(currentOrder.rows[rowIndex] as any)[rowProp] = localOrderBody.rows[rowIndex][rowProp];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (let key in this.store.orderFooter) {
|
||||
if (localOrder.orderType == 'orderO') {
|
||||
const currentOrder = this.store[localOrder.orderType];
|
||||
|
||||
for (let rowIndex = 0; rowIndex < currentOrder.orderList.length; rowIndex++) {
|
||||
const row = currentOrder.orderList[rowIndex];
|
||||
|
||||
if (localOrderBody.orderList[rowIndex] === undefined) {
|
||||
alertWrongOrderFormat();
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const rowProp in row) {
|
||||
if (localOrderBody.orderList[rowIndex][rowProp] === undefined) {
|
||||
alertWrongOrderFormat();
|
||||
continue;
|
||||
}
|
||||
|
||||
(currentOrder.orderList[rowIndex] as any)[rowProp] =
|
||||
localOrderBody.orderList[rowIndex][rowProp];
|
||||
}
|
||||
}
|
||||
|
||||
currentOrder.other = localOrderBody.other;
|
||||
}
|
||||
|
||||
for (const key in this.store.orderFooter) {
|
||||
(this.store.orderFooter as any)[key] = localOrderFooter[key];
|
||||
}
|
||||
|
||||
this.store.orderMode = 'OrderMessage';
|
||||
},
|
||||
},
|
||||
this.store.chosenOrderType = localOrder.orderType;
|
||||
this.store.chosenLocalOrderId = localOrder.id;
|
||||
this.store.panelMode = 'OrderMessagePanel';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -27,9 +27,6 @@ export default defineComponent({
|
||||
fieldsToCorrect.push('dyżurny ruchu (lub z polecenia dyżurnego ruchu)');
|
||||
|
||||
return fieldsToCorrect;
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
|
||||
import Home from '../views/Home.vue'
|
||||
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';
|
||||
import Home from '../views/Home.vue';
|
||||
|
||||
const routes: Array<RouteRecordRaw> = [
|
||||
{
|
||||
path: '/',
|
||||
name: 'Home',
|
||||
component: Home
|
||||
},
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(),
|
||||
routes
|
||||
})
|
||||
});
|
||||
|
||||
export default router
|
||||
export default router;
|
||||
|
||||
@@ -1,24 +1,41 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { IOrderN, IOrderO, IOrderS, TOrder } from '../types/orderTypes';
|
||||
import { currentFormattedDate } from '../utils/dateUtils';
|
||||
import { IOrderN, IOrderO, IOrderS, TOrder, TPanel } from '../types/orderTypes';
|
||||
import {
|
||||
currentFormattedDate,
|
||||
currentFormattedHours,
|
||||
currentFormattedMinutes
|
||||
} from '../utils/dateUtils';
|
||||
import i18n from '../i18n';
|
||||
import StorageManager from '../managers/storageManager';
|
||||
|
||||
export const useStore = defineStore('store', {
|
||||
state: () => {
|
||||
return {
|
||||
currentAppLocale: 'pl',
|
||||
|
||||
appUpdateData: {
|
||||
version: '',
|
||||
changelog: '',
|
||||
releaseURL: ''
|
||||
},
|
||||
|
||||
isMigrationInfoOpen: false,
|
||||
updateCardOpen: false,
|
||||
helperModalOpen: false,
|
||||
orderDarkMode: false,
|
||||
|
||||
chosenOrderType: 'orderN' as TOrder,
|
||||
chosenLocalOrderId: '',
|
||||
|
||||
orderMode: 'OrderMessage',
|
||||
panelMode: 'OrderMessagePanel' as TPanel,
|
||||
|
||||
orderFooter: {
|
||||
stationName: '',
|
||||
checkpointName: '',
|
||||
hour: new Date().toLocaleTimeString('pl-PL', { hour: '2-digit' }),
|
||||
minutes: new Date().toLocaleTimeString('pl-PL', { minute: '2-digit' }),
|
||||
hour: currentFormattedHours(),
|
||||
minutes: currentFormattedMinutes(),
|
||||
dispatcherName: '',
|
||||
secondaryDispatcherName: '',
|
||||
secondaryDispatcherName: ''
|
||||
},
|
||||
|
||||
orderMessage: '',
|
||||
@@ -28,7 +45,7 @@ export const useStore = defineStore('store', {
|
||||
header: {
|
||||
orderNo: '1',
|
||||
trainNo: '',
|
||||
date: currentFormattedDate(),
|
||||
date: currentFormattedDate()
|
||||
},
|
||||
|
||||
orderList: [
|
||||
@@ -38,7 +55,7 @@ export const useStore = defineStore('store', {
|
||||
to: '',
|
||||
vmax: '',
|
||||
jo: false,
|
||||
reason: '',
|
||||
reason: ''
|
||||
},
|
||||
{
|
||||
name: '',
|
||||
@@ -46,7 +63,7 @@ export const useStore = defineStore('store', {
|
||||
to: '',
|
||||
vmax: '',
|
||||
jo: false,
|
||||
reason: '',
|
||||
reason: ''
|
||||
},
|
||||
{
|
||||
name: '',
|
||||
@@ -54,7 +71,7 @@ export const useStore = defineStore('store', {
|
||||
to: '',
|
||||
vmax: '',
|
||||
jo: false,
|
||||
reason: '',
|
||||
reason: ''
|
||||
},
|
||||
{
|
||||
name: '',
|
||||
@@ -62,7 +79,7 @@ export const useStore = defineStore('store', {
|
||||
to: '',
|
||||
vmax: '',
|
||||
jo: false,
|
||||
reason: '',
|
||||
reason: ''
|
||||
},
|
||||
{
|
||||
name: '',
|
||||
@@ -70,17 +87,17 @@ export const useStore = defineStore('store', {
|
||||
to: '',
|
||||
vmax: '',
|
||||
jo: false,
|
||||
reason: '',
|
||||
},
|
||||
reason: ''
|
||||
}
|
||||
],
|
||||
other: '',
|
||||
other: ''
|
||||
} as IOrderO,
|
||||
|
||||
orderN: {
|
||||
header: {
|
||||
orderNo: '1',
|
||||
trainNo: '',
|
||||
date: currentFormattedDate(),
|
||||
date: currentFormattedDate()
|
||||
},
|
||||
|
||||
rows: [
|
||||
@@ -89,7 +106,7 @@ export const useStore = defineStore('store', {
|
||||
from: '',
|
||||
to: '',
|
||||
trackNo: '',
|
||||
trackNo2: '',
|
||||
trackNo2: ''
|
||||
},
|
||||
{
|
||||
enabled: false,
|
||||
@@ -105,7 +122,7 @@ export const useStore = defineStore('store', {
|
||||
direction2: '',
|
||||
trackNoFrom: '',
|
||||
trackNoTo1: '',
|
||||
trackNoTo2: '',
|
||||
trackNoTo2: ''
|
||||
},
|
||||
{
|
||||
enabled: false,
|
||||
@@ -116,7 +133,7 @@ export const useStore = defineStore('store', {
|
||||
toKilometer: '',
|
||||
trackNo: '',
|
||||
untilHour: '',
|
||||
untilMin: '',
|
||||
untilMin: ''
|
||||
},
|
||||
{
|
||||
enabled: false,
|
||||
@@ -124,7 +141,7 @@ export const useStore = defineStore('store', {
|
||||
optionStation: 'stację',
|
||||
stationName: '',
|
||||
checkbox: 'checkbox-4a',
|
||||
side: 'lewej',
|
||||
side: 'lewej'
|
||||
},
|
||||
{
|
||||
enabled: false,
|
||||
@@ -132,9 +149,19 @@ export const useStore = defineStore('store', {
|
||||
direction: '',
|
||||
stationType: 'stację',
|
||||
stationName: '',
|
||||
on: '',
|
||||
on: ''
|
||||
},
|
||||
],
|
||||
{
|
||||
enabled: false,
|
||||
content: '',
|
||||
twoWay: {
|
||||
enabled: false,
|
||||
from: '',
|
||||
to: '',
|
||||
trackNo: ''
|
||||
}
|
||||
}
|
||||
]
|
||||
} as IOrderN,
|
||||
|
||||
orderS: {
|
||||
@@ -142,7 +169,7 @@ export const useStore = defineStore('store', {
|
||||
orderNo: '1',
|
||||
trainNo: '',
|
||||
for: 'pociągu',
|
||||
date: currentFormattedDate(),
|
||||
date: currentFormattedDate()
|
||||
},
|
||||
|
||||
rows: [
|
||||
@@ -152,7 +179,7 @@ export const useStore = defineStore('store', {
|
||||
optionSignal: 'wyjazdowego',
|
||||
radio1: 'radio-1a-1',
|
||||
signal1: '',
|
||||
trackNo: '',
|
||||
trackNo: ''
|
||||
},
|
||||
|
||||
{
|
||||
@@ -161,7 +188,7 @@ export const useStore = defineStore('store', {
|
||||
signal1: '',
|
||||
signal2: '',
|
||||
signal3: '',
|
||||
trackNo: '',
|
||||
trackNo: ''
|
||||
},
|
||||
|
||||
{
|
||||
@@ -171,16 +198,32 @@ export const useStore = defineStore('store', {
|
||||
trackNo: '',
|
||||
trainNo: '',
|
||||
arrivedTo: '',
|
||||
hour: '',
|
||||
hour: ''
|
||||
},
|
||||
|
||||
{
|
||||
enabled: false,
|
||||
content: '',
|
||||
},
|
||||
],
|
||||
} as IOrderS,
|
||||
w5: {
|
||||
enabled: false,
|
||||
maxHour: '',
|
||||
borderType: 'wskaźnik przetaczania W5',
|
||||
tmName: '',
|
||||
maxKm: '',
|
||||
returnWay: 'sygnał ręczny "Do mnie"',
|
||||
trackNo: ''
|
||||
}
|
||||
}
|
||||
]
|
||||
} as IOrderS
|
||||
};
|
||||
},
|
||||
});
|
||||
actions: {
|
||||
changeLang(lang: string) {
|
||||
i18n.global.locale.value = lang as typeof i18n.global.locale.value;
|
||||
this.currentAppLocale = lang;
|
||||
|
||||
StorageManager.setStringValue('lang', lang);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -10,4 +10,3 @@
|
||||
transform: translateY(100%);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
$bgCol: #141414;
|
||||
$bgColLighter: #292929;
|
||||
$bgColDarker: #080808;
|
||||
$accentCol: #ff6060;
|
||||
$warnCol: #ffe02e;
|
||||
$whiteDimmerCol: #ccc;
|
||||
@@ -0,0 +1,31 @@
|
||||
@font-face {
|
||||
font-display: swap;
|
||||
font-family: 'Libre Franklin';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url('/fonts/libre-franklin-regular.woff2') format('woff2');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-display: swap;
|
||||
font-family: 'Libre Franklin';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: url('/fonts/libre-franklin-500.woff2') format('woff2');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-display: swap;
|
||||
font-family: 'Libre Franklin';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: url('/fonts/libre-franklin-700.woff2') format('woff2');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-display: swap;
|
||||
font-family: 'Libre Franklin';
|
||||
font-style: normal;
|
||||
font-weight: 800;
|
||||
src: url('/fonts/libre-franklin-800.woff2') format('woff2');
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
@use 'fonts';
|
||||
@use 'colors';
|
||||
|
||||
[data-theme='dark'] {
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
body,
|
||||
html {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
min-height: 100vh;
|
||||
background-color: colors.$bgCol;
|
||||
|
||||
font-family: 'Libre Franklin', sans-serif;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
a {
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
|
||||
&:hover {
|
||||
color: colors.$accentCol;
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
border: none;
|
||||
outline: none;
|
||||
background: none;
|
||||
|
||||
transition:
|
||||
color 90ms ease-in,
|
||||
border 90ms ease-in;
|
||||
font-family: 'Libre Franklin', sans-serif;
|
||||
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
button.g-button {
|
||||
text-align: center;
|
||||
color: inherit;
|
||||
|
||||
transition:
|
||||
color 100ms ease-in,
|
||||
background-color 100ms ease-in;
|
||||
|
||||
&.action {
|
||||
background-color: colors.$bgColDarker;
|
||||
padding: 0.5em;
|
||||
color: white;
|
||||
|
||||
&:focus-visible {
|
||||
outline: 2px solid colors.$accentCol;
|
||||
}
|
||||
|
||||
&:hover:not([data-disabled='true']) {
|
||||
background-color: colors.$bgColLighter;
|
||||
}
|
||||
}
|
||||
|
||||
&.option {
|
||||
position: relative;
|
||||
margin: 0 0.25em;
|
||||
padding: 0.25em;
|
||||
|
||||
&:focus-visible::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
background-color: colors.$accentCol;
|
||||
}
|
||||
|
||||
&[data-active='true'] {
|
||||
color: colors.$accentCol;
|
||||
}
|
||||
}
|
||||
|
||||
&.text {
|
||||
padding: 0;
|
||||
|
||||
&:focus-visible {
|
||||
color: colors.$accentCol;
|
||||
}
|
||||
}
|
||||
|
||||
&.icon {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
// Text styles
|
||||
.text {
|
||||
&--accent {
|
||||
color: colors.$accentCol;
|
||||
}
|
||||
|
||||
&--warn {
|
||||
color: colors.$warnCol;
|
||||
}
|
||||
}
|
||||
|
||||
// Select style
|
||||
select {
|
||||
border: 2px solid black;
|
||||
background: none;
|
||||
padding: 0.1em 0;
|
||||
border-radius: 0.3em;
|
||||
text-align: center;
|
||||
|
||||
border: 2px solid #888;
|
||||
color: white;
|
||||
outline: none;
|
||||
|
||||
margin: 0;
|
||||
padding: 0.15em;
|
||||
|
||||
&[disabled] {
|
||||
color: gray;
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
border-color: colors.$accentCol;
|
||||
}
|
||||
}
|
||||
|
||||
option[disabled] {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
// List style
|
||||
ul {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
|
||||
list-style: none;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
// Modal
|
||||
.g-modal {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
z-index: 1000;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.modal-bg {
|
||||
position: fixed;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
|
||||
cursor: pointer;
|
||||
|
||||
background-color: #000000aa;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
position: relative;
|
||||
z-index: 1001;
|
||||
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
|
||||
// Checkbox
|
||||
label.g-checkbox {
|
||||
display: inline-block;
|
||||
margin: 0.25em 0;
|
||||
cursor: pointer;
|
||||
color: #aaa;
|
||||
|
||||
user-select: none;
|
||||
-moz-user-select: none;
|
||||
|
||||
span {
|
||||
transition: color 125ms ease;
|
||||
}
|
||||
|
||||
span::before {
|
||||
content: '\2717';
|
||||
display: inline-block;
|
||||
|
||||
// background-color: #aaa;
|
||||
border-radius: 50%;
|
||||
margin-right: 0.25em;
|
||||
|
||||
transition: background-color 125ms ease;
|
||||
}
|
||||
|
||||
input {
|
||||
width: 0;
|
||||
opacity: 0;
|
||||
|
||||
&:focus-visible + span {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
&:checked + span {
|
||||
color: greenyellow;
|
||||
|
||||
&::before {
|
||||
content: '\2713';
|
||||
// background-color: greenyellow;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tooltip
|
||||
[data-tooltip] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
[data-tooltip]:hover::after,
|
||||
[data-tooltip]:focus::after {
|
||||
position: absolute;
|
||||
|
||||
content: attr(data-tooltip);
|
||||
color: white;
|
||||
background: black;
|
||||
padding: 0.5em;
|
||||
margin: 0.25em;
|
||||
max-width: 300px;
|
||||
z-index: 100;
|
||||
}
|
||||
@@ -1,145 +0,0 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Libre+Franklin:wght@400;600&display=swap');
|
||||
|
||||
$bgCol: #313638;
|
||||
$accentCol: #ff6060;
|
||||
$warnCol: #ffe02e;
|
||||
|
||||
body,
|
||||
html {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
min-height: 100vh;
|
||||
background-color: $bgCol;
|
||||
|
||||
font-family: 'Libre Franklin', sans-serif;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
a {
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
|
||||
&:hover {
|
||||
color: $accentCol;
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
border: none;
|
||||
outline: none;
|
||||
background: none;
|
||||
|
||||
transition: all 150ms ease-in;
|
||||
font-family: 'Libre Franklin', sans-serif;
|
||||
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
button.g-button {
|
||||
text-align: center;
|
||||
color: white;
|
||||
|
||||
&.action {
|
||||
outline: 2px solid white;
|
||||
padding: 0.5em;
|
||||
|
||||
&:focus-visible {
|
||||
outline: 2px solid $accentCol;
|
||||
}
|
||||
}
|
||||
|
||||
&.option {
|
||||
margin: 0 0.25em;
|
||||
|
||||
&:focus-visible {
|
||||
outline: 1px solid $accentCol;
|
||||
}
|
||||
|
||||
&[data-active='true'] {
|
||||
color: $accentCol;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Text styles
|
||||
.text {
|
||||
&--accent {
|
||||
color: $accentCol;
|
||||
}
|
||||
|
||||
&--warn {
|
||||
color: $warnCol;
|
||||
}
|
||||
}
|
||||
|
||||
// Select style
|
||||
select {
|
||||
border: 2px solid black;
|
||||
background: none;
|
||||
padding: 0.1em 0;
|
||||
border-radius: 0.3em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
// List style
|
||||
ul {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
|
||||
list-style: none;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
// Global scrollbar style
|
||||
::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: #333;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: #888;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: #555;
|
||||
}
|
||||
|
||||
// Modal
|
||||
.g-modal {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
z-index: 1000;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.modal-bg {
|
||||
position: fixed;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
|
||||
cursor: pointer;
|
||||
|
||||
background-color: #000000aa;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
position: relative;
|
||||
z-index: 1001;
|
||||
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
@@ -1,117 +1,106 @@
|
||||
export declare module ApiSWDR {
|
||||
export interface IStationsOnline {
|
||||
success: boolean;
|
||||
respCode: number;
|
||||
message: IStationsOnlineMessage[];
|
||||
export declare module API {
|
||||
export namespace ActiveData {
|
||||
export interface Response {
|
||||
activeSceneries?: API.ActiveSceneries.Response;
|
||||
trains?: API.ActiveTrains.Response;
|
||||
}
|
||||
}
|
||||
|
||||
export interface IStationsOnlineMessage {
|
||||
dispatcherId: number;
|
||||
dispatcherName: string;
|
||||
dispatcherIsSupporter: boolean;
|
||||
stationName: string;
|
||||
stationHash: string;
|
||||
region: string;
|
||||
maxUsers: number;
|
||||
currentUsers: number;
|
||||
spawn: number;
|
||||
lastSeen: any;
|
||||
dispatcherExp: number;
|
||||
nameFromHeader: string;
|
||||
spawnString: string;
|
||||
networkConnectionString: string;
|
||||
isOnline: number;
|
||||
dispatcherRate: number;
|
||||
export namespace ActiveSceneries {
|
||||
export interface Data {
|
||||
dispatcherId: number;
|
||||
dispatcherName: string;
|
||||
dispatcherIsSupporter: boolean;
|
||||
stationName: string;
|
||||
stationHash: string;
|
||||
region: string;
|
||||
maxUsers: number;
|
||||
currentUsers: number;
|
||||
spawn: number;
|
||||
lastSeen: number;
|
||||
dispatcherExp: number;
|
||||
nameFromHeader: string;
|
||||
spawnString: string | null;
|
||||
networkConnectionString: string;
|
||||
isOnline: number;
|
||||
dispatcherRate: number;
|
||||
dispatcherStatus: number;
|
||||
}
|
||||
|
||||
export type Response = Data[];
|
||||
}
|
||||
|
||||
// export interface ITrainsOnline {
|
||||
// success: boolean;
|
||||
// respCode: number;
|
||||
// message: ITrainsOnlineMessage[];
|
||||
// }
|
||||
export namespace ActiveTrains {
|
||||
export type Response = Data[];
|
||||
|
||||
// export interface ITrainsOnlineMessage {
|
||||
// trainNo: number;
|
||||
// driverId: number;
|
||||
// driverName: string;
|
||||
// driverIsSupporter: boolean;
|
||||
// dataSignal: string;
|
||||
// dataSceneryConnection: string;
|
||||
// dataDistance: number;
|
||||
// dataCon: string;
|
||||
// dataSpeed: number;
|
||||
// dataMass: number;
|
||||
// dataLength: number;
|
||||
// region: string;
|
||||
// isOnline: number;
|
||||
// lastSeen: number;
|
||||
// station?: ISceneryData;
|
||||
// }
|
||||
}
|
||||
export interface Data {
|
||||
trainNo: number;
|
||||
|
||||
export declare module ApiStacjownik {
|
||||
export interface IActiveTrain {
|
||||
trainNo: number;
|
||||
mass: number;
|
||||
length: number;
|
||||
speed: number;
|
||||
stockString: string;
|
||||
|
||||
mass: number;
|
||||
length: number;
|
||||
speed: number;
|
||||
signal: string;
|
||||
distance: number;
|
||||
connectedTrack: string;
|
||||
|
||||
signal: string;
|
||||
distance: number;
|
||||
connectedTrack: string;
|
||||
stockString: string;
|
||||
driverName: string;
|
||||
driverId: number;
|
||||
driverIsSupporter: boolean;
|
||||
driverLevel?: number;
|
||||
|
||||
driverName: string;
|
||||
driverId: number;
|
||||
driverIsSupporter: boolean;
|
||||
currentStationName: string;
|
||||
currentStationHash?: string;
|
||||
|
||||
currentStationName: string;
|
||||
currentStationHash?: string;
|
||||
online: number;
|
||||
lastSeen: number;
|
||||
|
||||
online: boolean;
|
||||
lastSeen: number;
|
||||
region: string;
|
||||
isTimeout: boolean;
|
||||
|
||||
region: string;
|
||||
timetable?: Timetable;
|
||||
}
|
||||
|
||||
timetable?: {
|
||||
export interface TimetableStop {
|
||||
stopName: string;
|
||||
stopNameRAW: string;
|
||||
stopType: string;
|
||||
stopDistance: number;
|
||||
pointId: string;
|
||||
|
||||
mainStop: boolean;
|
||||
|
||||
arrivalLine: string | null;
|
||||
arrivalTimestamp: number;
|
||||
arrivalRealTimestamp: number;
|
||||
arrivalDelay: number;
|
||||
|
||||
departureLine: string | null;
|
||||
departureTimestamp: number;
|
||||
departureRealTimestamp: number;
|
||||
departureDelay: number;
|
||||
|
||||
comments?: any;
|
||||
|
||||
beginsHere: boolean;
|
||||
terminatesHere: boolean;
|
||||
confirmed: number;
|
||||
stopped: number;
|
||||
stopTime: number | null;
|
||||
}
|
||||
|
||||
export interface Timetable {
|
||||
timetableId: number;
|
||||
category: string;
|
||||
route: string;
|
||||
stopList: IActiveTrainStop[];
|
||||
|
||||
stopList: TimetableStop[];
|
||||
|
||||
TWR: boolean;
|
||||
SKR: boolean;
|
||||
sceneries: string[];
|
||||
};
|
||||
|
||||
isTimeout: boolean;
|
||||
path: string;
|
||||
}
|
||||
}
|
||||
|
||||
export interface IActiveTrainStop {
|
||||
stopName: string;
|
||||
stopNameRAW: string;
|
||||
stopType: string;
|
||||
stopDistance: number;
|
||||
pointId: number;
|
||||
|
||||
mainStop: boolean;
|
||||
|
||||
arrivalLine: string;
|
||||
arrivalTimestamp: number;
|
||||
arrivalRealTimestamp: number;
|
||||
arrivalDelay: number;
|
||||
|
||||
departureLine: string;
|
||||
departureTimestamp: number;
|
||||
departureRealTimestamp: number;
|
||||
departureDelay: number;
|
||||
|
||||
comments?: any;
|
||||
|
||||
beginsHere: boolean;
|
||||
terminatesHere: boolean;
|
||||
confirmed: boolean;
|
||||
stopped: boolean;
|
||||
stopTime: number;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ export interface ISceneryOnline {
|
||||
export interface ISceneryData {
|
||||
id: string;
|
||||
name: string;
|
||||
abbr: string;
|
||||
SUP: boolean;
|
||||
authors: string;
|
||||
availability: string;
|
||||
@@ -49,5 +50,3 @@ export interface ISceneryData {
|
||||
// lastSeen: number;
|
||||
// station?: ISceneryData;
|
||||
// }
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export type TOrder = 'orderO' | 'orderS' | 'orderN';
|
||||
export type TPanel = 'OrderMessagePanel' | 'OrderListPanel' | 'OrderTrainPickerPanel';
|
||||
|
||||
export interface LocalStorageOrder {
|
||||
id: string;
|
||||
@@ -7,6 +8,7 @@ export interface LocalStorageOrder {
|
||||
orderFooter: any;
|
||||
createdAt?: number;
|
||||
updatedAt?: number;
|
||||
orderVersion?: string;
|
||||
}
|
||||
|
||||
export interface IOrderN {
|
||||
@@ -66,6 +68,16 @@ export interface IOrderN {
|
||||
stationType: string;
|
||||
stationName: string;
|
||||
on: string;
|
||||
},
|
||||
{
|
||||
enabled: boolean;
|
||||
content: string;
|
||||
twoWay: {
|
||||
enabled: boolean;
|
||||
from: string;
|
||||
to: string;
|
||||
trackNo: string;
|
||||
};
|
||||
}
|
||||
];
|
||||
}
|
||||
@@ -107,6 +119,16 @@ export interface IOrderS {
|
||||
{
|
||||
enabled: boolean;
|
||||
content: string;
|
||||
|
||||
w5: {
|
||||
enabled: boolean;
|
||||
borderType: string;
|
||||
trackNo: string;
|
||||
maxKm: string;
|
||||
returnWay: string;
|
||||
maxHour: string;
|
||||
tmName: string;
|
||||
};
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
@@ -1,3 +1,18 @@
|
||||
export function currentFormattedDate() {
|
||||
return new Date().toLocaleDateString('pl-PL', { day: 'numeric', month: 'numeric', year: 'numeric' }) + 'r.';
|
||||
}
|
||||
return (
|
||||
new Date().toLocaleDateString('pl-PL', {
|
||||
day: 'numeric',
|
||||
month: 'numeric',
|
||||
year: 'numeric'
|
||||
}) + 'r.'
|
||||
);
|
||||
}
|
||||
|
||||
export function currentFormattedMinutes() {
|
||||
const date = new Date();
|
||||
return (date.getMinutes() < 10 ? '0' : '') + date.getMinutes();
|
||||
}
|
||||
|
||||
export function currentFormattedHours() {
|
||||
return new Date().toLocaleTimeString('pl-PL', { hour: '2-digit' });
|
||||
}
|
||||
|
||||
@@ -0,0 +1,197 @@
|
||||
import { IOrderN, IOrderO, IOrderS, TOrder } from '../types/orderTypes';
|
||||
import { currentFormattedDate } from './dateUtils';
|
||||
|
||||
const orderDefaults = {
|
||||
orderN: {
|
||||
header: {
|
||||
orderNo: '1',
|
||||
trainNo: '',
|
||||
date: ''
|
||||
},
|
||||
|
||||
rows: [
|
||||
{
|
||||
enabled: false,
|
||||
from: '',
|
||||
to: '',
|
||||
trackNo: '',
|
||||
trackNo2: ''
|
||||
},
|
||||
{
|
||||
enabled: false,
|
||||
option1: 'sygnału "Nakaz Jazdy"',
|
||||
option2: 'lewy',
|
||||
option3: 'lewy',
|
||||
signal1: '',
|
||||
signal2: '',
|
||||
signal3: '',
|
||||
signalType: 'wyjazdowego',
|
||||
checkbox: 'checkbox-2a',
|
||||
direction1: '',
|
||||
direction2: '',
|
||||
trackNoFrom: '',
|
||||
trackNoTo1: '',
|
||||
trackNoTo2: ''
|
||||
},
|
||||
{
|
||||
enabled: false,
|
||||
option1: 'Jazda',
|
||||
option2: 'pociąg',
|
||||
|
||||
direction: '',
|
||||
toKilometer: '',
|
||||
trackNo: '',
|
||||
untilHour: '',
|
||||
untilMin: ''
|
||||
},
|
||||
{
|
||||
enabled: false,
|
||||
trackNo: '',
|
||||
optionStation: 'stację',
|
||||
stationName: '',
|
||||
checkbox: 'checkbox-4a',
|
||||
side: 'lewej'
|
||||
},
|
||||
{
|
||||
enabled: false,
|
||||
trackNo: '',
|
||||
direction: '',
|
||||
stationType: 'stację',
|
||||
stationName: '',
|
||||
on: ''
|
||||
},
|
||||
{
|
||||
enabled: false,
|
||||
content: '',
|
||||
twoWay: {
|
||||
enabled: false,
|
||||
from: '',
|
||||
to: '',
|
||||
trackNo: ''
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
orderS: {
|
||||
header: {
|
||||
orderNo: '1',
|
||||
trainNo: '',
|
||||
for: 'pociągu',
|
||||
date: ''
|
||||
},
|
||||
|
||||
rows: [
|
||||
{
|
||||
enabled: false,
|
||||
option1: 'sygnału "nakaz jazdy"',
|
||||
optionSignal: 'wyjazdowego',
|
||||
radio1: 'radio-1a-1',
|
||||
signal1: '',
|
||||
trackNo: ''
|
||||
},
|
||||
|
||||
{
|
||||
enabled: false,
|
||||
signalType: 'wyjazdowego',
|
||||
signal1: '',
|
||||
signal2: '',
|
||||
signal3: '',
|
||||
trackNo: ''
|
||||
},
|
||||
|
||||
{
|
||||
enabled: false,
|
||||
from: '',
|
||||
to: '',
|
||||
trackNo: '',
|
||||
trainNo: '',
|
||||
arrivedTo: '',
|
||||
hour: ''
|
||||
},
|
||||
|
||||
{
|
||||
enabled: false,
|
||||
content: '',
|
||||
w5: {
|
||||
enabled: false,
|
||||
maxHour: '',
|
||||
borderType: 'wskaźnik przetaczania W5',
|
||||
tmName: '',
|
||||
maxKm: '',
|
||||
returnWay: 'sygnał ręczny "Do mnie"',
|
||||
trackNo: ''
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
orderO: {
|
||||
header: {
|
||||
orderNo: '1',
|
||||
trainNo: '',
|
||||
date: ''
|
||||
},
|
||||
|
||||
orderList: [
|
||||
{
|
||||
name: '',
|
||||
from: '',
|
||||
to: '',
|
||||
vmax: '',
|
||||
jo: false,
|
||||
reason: ''
|
||||
},
|
||||
{
|
||||
name: '',
|
||||
from: '',
|
||||
to: '',
|
||||
vmax: '',
|
||||
jo: false,
|
||||
reason: ''
|
||||
},
|
||||
{
|
||||
name: '',
|
||||
from: '',
|
||||
to: '',
|
||||
vmax: '',
|
||||
jo: false,
|
||||
reason: ''
|
||||
},
|
||||
{
|
||||
name: '',
|
||||
from: '',
|
||||
to: '',
|
||||
vmax: '',
|
||||
jo: false,
|
||||
reason: ''
|
||||
},
|
||||
{
|
||||
name: '',
|
||||
from: '',
|
||||
to: '',
|
||||
vmax: '',
|
||||
jo: false,
|
||||
reason: ''
|
||||
}
|
||||
],
|
||||
other: ''
|
||||
}
|
||||
};
|
||||
|
||||
export function getOrderType(order: IOrderN | IOrderO | IOrderS): TOrder {
|
||||
if ('rows' in order && 'for' in order.header) return 'orderS';
|
||||
else if ('rows' in order) return 'orderN';
|
||||
|
||||
return 'orderO';
|
||||
}
|
||||
|
||||
export function setOrderToDefault(order: IOrderN | IOrderO | IOrderS) {
|
||||
const orderType = getOrderType(order);
|
||||
const defaultOrderObjectCopy = JSON.parse(JSON.stringify(orderDefaults[orderType]));
|
||||
|
||||
Object.assign(order, defaultOrderObjectCopy);
|
||||
|
||||
// Update date in the header
|
||||
order.header.date = currentFormattedDate();
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
export const getRegionNameById = (id: string) => {
|
||||
switch (id) {
|
||||
case 'eu':
|
||||
return 'PL1';
|
||||
|
||||
case 'cae':
|
||||
return 'PL2';
|
||||
|
||||
case 'us':
|
||||
return 'CZE';
|
||||
|
||||
case 'usw':
|
||||
return 'DE';
|
||||
|
||||
case 'ru':
|
||||
return 'ENG';
|
||||
|
||||
default:
|
||||
return 'PL1';
|
||||
}
|
||||
};
|
||||
@@ -2,30 +2,45 @@
|
||||
<!-- <OrderHelper v-if="store.helperModalOpen" /> -->
|
||||
|
||||
<div class="home">
|
||||
<div class="home_container">
|
||||
<div class="order_container">
|
||||
<SideBar />
|
||||
<OrderVue />
|
||||
</div>
|
||||
<div class="home-container">
|
||||
<Order />
|
||||
|
||||
<div class="message_container">
|
||||
<div class="message_nav">
|
||||
<span v-for="(action, i) in navActions">
|
||||
<b v-if="i > 0">•</b>
|
||||
<div class="panel-container">
|
||||
<div class="panel-nav">
|
||||
<button
|
||||
key="OrderMessagePanel"
|
||||
class="g-button"
|
||||
:data-active="store.panelMode == 'OrderMessagePanel'"
|
||||
@click="selectPanelMode('OrderMessagePanel')"
|
||||
>
|
||||
<MessageSquareTextIcon :size="20" />
|
||||
{{ t(`navbar.OrderMessagePanel`) }}
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="g-button option"
|
||||
:data-active="store.orderMode == action.mode"
|
||||
@click="selectOrderMode(action.mode)"
|
||||
>
|
||||
{{ action.value }}
|
||||
</button>
|
||||
</span>
|
||||
<button
|
||||
key="OrderListPanel"
|
||||
class="g-button"
|
||||
:data-active="store.panelMode == 'OrderListPanel'"
|
||||
@click="selectPanelMode('OrderListPanel')"
|
||||
>
|
||||
<BookMarkedIcon :size="20" />
|
||||
{{ t(`navbar.OrderListPanel`) }}
|
||||
</button>
|
||||
|
||||
<button
|
||||
key="OrderTrainPickerPanel"
|
||||
class="g-button"
|
||||
:data-active="store.panelMode == 'OrderTrainPickerPanel'"
|
||||
@click="selectPanelMode('OrderTrainPickerPanel')"
|
||||
>
|
||||
<TrainFrontIcon :size="20" />
|
||||
{{ t(`navbar.OrderTrainPickerPanel`) }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<transition name="order-anim" mode="out-in">
|
||||
<keep-alive>
|
||||
<Component :is="orderModeComponent" />
|
||||
<Component :is="panelComponent" />
|
||||
</keep-alive>
|
||||
</transition>
|
||||
</div>
|
||||
@@ -33,71 +48,42 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import OrderVue from '../components/Order.vue';
|
||||
import SideBar from '../components/SideBar.vue';
|
||||
import OrderMessage from '../components/OrderMessage.vue';
|
||||
import OrderList from '../components/OrderList.vue';
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useStore } from '../store/store';
|
||||
import OrderHelper from '../components/OrderHelper.vue';
|
||||
import OrderTrainPicker from '../components/OrderTrainPicker.vue';
|
||||
import OrderMessagePanel from '../components/Panels/OrderMessagePanel.vue';
|
||||
import OrderListPanel from '../components/Panels/OrderListPanel.vue';
|
||||
import OrderTrainPickerPanel from '../components/Panels/OrderTrainPickerPanel.vue';
|
||||
import SideBar from '../components/App/SideBar.vue';
|
||||
import Order from '../components/Orders/Order.vue';
|
||||
import { BookMarkedIcon, MessageSquareTextIcon, TrainFrontIcon } from 'lucide-vue-next';
|
||||
import { TPanel } from '../types/orderTypes';
|
||||
|
||||
const store = useStore();
|
||||
const { t } = useI18n();
|
||||
|
||||
export default defineComponent({
|
||||
components: { OrderVue, SideBar, OrderHelper },
|
||||
function selectPanelMode(mode: TPanel) {
|
||||
store.panelMode = mode;
|
||||
}
|
||||
|
||||
data() {
|
||||
return {
|
||||
navActions: [
|
||||
{
|
||||
mode: 'OrderMessage',
|
||||
value: 'TREŚĆ ROZKAZU',
|
||||
},
|
||||
{
|
||||
mode: 'OrderList',
|
||||
value: 'ZAPISANE ROZKAZY',
|
||||
},
|
||||
{
|
||||
mode: 'OrderTrainPicker',
|
||||
value: 'POCIĄGI',
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
selectOrderMode(mode: string) {
|
||||
this.store.orderMode = mode;
|
||||
},
|
||||
},
|
||||
|
||||
setup() {
|
||||
return {
|
||||
store: useStore(),
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
orderModeComponent() {
|
||||
switch (this.store.orderMode) {
|
||||
case 'OrderMessage':
|
||||
return OrderMessage;
|
||||
case 'OrderList':
|
||||
return OrderList;
|
||||
case 'OrderTrainPicker':
|
||||
return OrderTrainPicker;
|
||||
default:
|
||||
return OrderMessage;
|
||||
}
|
||||
},
|
||||
},
|
||||
const panelComponent = computed(() => {
|
||||
switch (store.panelMode) {
|
||||
case 'OrderListPanel':
|
||||
return OrderListPanel;
|
||||
case 'OrderTrainPickerPanel':
|
||||
return OrderTrainPickerPanel;
|
||||
case 'OrderMessagePanel':
|
||||
default:
|
||||
return OrderMessagePanel;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@use '../styles/colors';
|
||||
|
||||
.home {
|
||||
min-height: 100vh;
|
||||
overflow-x: auto;
|
||||
|
||||
display: flex;
|
||||
@@ -105,38 +91,77 @@ export default defineComponent({
|
||||
align-items: center;
|
||||
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.home_container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 2em 1em;
|
||||
padding: 0.5em;
|
||||
.home-container {
|
||||
display: grid;
|
||||
grid-template-columns: 600px 500px;
|
||||
justify-content: center;
|
||||
gap: 2em 1em;
|
||||
padding: 1em;
|
||||
width: 100%;
|
||||
|
||||
@media screen and (max-width: 1150px) {
|
||||
grid-template-columns: auto;
|
||||
padding: 1em 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
.panel-container {
|
||||
display: grid;
|
||||
grid-template-rows: auto auto 1fr;
|
||||
color-scheme: dark;
|
||||
|
||||
padding: 2px;
|
||||
max-width: 800px;
|
||||
height: calc(100vh - 5em);
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.panel-nav {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.25em;
|
||||
margin-bottom: 1.5em;
|
||||
}
|
||||
|
||||
.panel-nav > button {
|
||||
position: relative;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 0.5em;
|
||||
min-width: 8em;
|
||||
padding: 0.25em 0.5em;
|
||||
|
||||
&:focus-visible {
|
||||
outline: 1px solid white;
|
||||
}
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
content: '';
|
||||
bottom: -3px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
|
||||
width: 0;
|
||||
height: 3px;
|
||||
|
||||
transition: all 0.25s;
|
||||
|
||||
background-color: colors.$accentCol;
|
||||
}
|
||||
|
||||
&[data-active='true'] {
|
||||
color: colors.$accentCol;
|
||||
}
|
||||
|
||||
&[data-active='true']::before {
|
||||
width: 100%;
|
||||
|
||||
@media screen and (max-width: 650px) {
|
||||
padding-top: 5em;
|
||||
padding-bottom: 5em;
|
||||
}
|
||||
}
|
||||
|
||||
.order_container {
|
||||
width: 100%;
|
||||
max-width: 550px;
|
||||
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.message_container {
|
||||
width: 500px;
|
||||
}
|
||||
|
||||
.message_nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
declare module '*.vue' {
|
||||
import type { DefineComponent } from 'vue'
|
||||
const component: DefineComponent<{}, {}, any>
|
||||
export default component
|
||||
import type { DefineComponent } from 'vue';
|
||||
const component: DefineComponent<{}, {}, any>;
|
||||
export default component;
|
||||
}
|
||||
|
||||
@@ -2,23 +2,34 @@ import { defineConfig } from 'vite';
|
||||
import vue from '@vitejs/plugin-vue';
|
||||
|
||||
import { VitePWA } from 'vite-plugin-pwa';
|
||||
import path from 'path';
|
||||
|
||||
export default defineConfig({
|
||||
server: {
|
||||
port: 8081,
|
||||
},
|
||||
css: {
|
||||
preprocessorOptions: {
|
||||
scss: { additionalData: `@use '@/styles/global';`, silenceDeprecations: ['legacy-js-api'] },
|
||||
},
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, 'src'),
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
vue(),
|
||||
VitePWA({
|
||||
registerType: 'prompt',
|
||||
workbox: {
|
||||
globPatterns: ['**/*.{js,css,html,png,svg,img}'],
|
||||
globPatterns: ['**/*.{js,css,html,png,svg,img,woff2,ico}'],
|
||||
runtimeCaching: [
|
||||
{
|
||||
urlPattern: /^https:\/\/stacjownik-api-b9mrc\.ondigitalocean\.app\/api\/getSceneries/i,
|
||||
urlPattern: /^https:\/\/stacjownik.spythere.eu\/\/api\/getSceneries/i,
|
||||
handler: 'CacheFirst',
|
||||
options: {
|
||||
cacheName: 'sceneries-data-cache',
|
||||
cacheName: 'sceneries-cache',
|
||||
expiration: {
|
||||
maxEntries: 250,
|
||||
maxAgeSeconds: 60 * 60 * 24 * 7, // <== 7 days
|
||||
@@ -31,9 +42,9 @@ export default defineConfig({
|
||||
],
|
||||
},
|
||||
devOptions: {
|
||||
enabled: true,
|
||||
// enabled: true,
|
||||
suppressWarnings: true
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
|
||||