Compare commits

..

40 Commits

Author SHA1 Message Date
Spythere ebfaf06a44 Merge pull request #72 from Spythere/development
Wersja 1.19.4
2023-12-11 13:02:05 +01:00
Spythere 5a651aedf8 imports hotfix 2023-12-11 12:58:41 +01:00
Spythere b66af014b9 lock sync 2023-12-10 15:29:29 +01:00
Spythere 634c9e1514 bump wersji: 1.19.4 2023-12-10 15:23:54 +01:00
Spythere c4132a9be2 cleanup http c.d. 2023-12-10 15:23:27 +01:00
Spythere 82a9a9165f cleanup http 2023-12-10 15:22:33 +01:00
Spythere fcac03c0a4 Merge pull request #71 from Spythere/development
Wersja 1.19.3
2023-12-10 00:56:21 +01:00
Spythere 39c3cf2329 restrukturyzacja storów 2023-12-09 16:18:23 +01:00
Spythere 59f4a0cb66 zmniejszenie czasu odpytywania z serwera 2023-12-08 20:00:17 +01:00
Spythere e2b42d16a4 poprawki scrollBehavior 2023-12-08 18:55:06 +01:00
Spythere e23663ed28 fix: brak ładowania danych o historii scenerii przy bezpośrednim wejściu z poz. URL 2023-12-08 18:15:49 +01:00
Spythere dc7846c31e bump wersji: 1.19.3 2023-12-08 17:15:16 +01:00
Spythere d875433d56 ulepszone zapamiętywanie zakładek statystyk w dzienniku 2023-12-08 17:14:49 +01:00
Spythere 71e5044cb4 poprawki designu dziennika RJ 2023-12-08 16:20:17 +01:00
Spythere e83aa40f82 aktualizacja endpointu API statystyk 2023-12-08 16:16:14 +01:00
Spythere d1c0e0b898 Merge pull request #70 from Spythere/development
Wersja 1.19.2
2023-12-07 16:29:53 +01:00
Spythere 26a7c69886 bump: 1.19.2 2023-12-07 16:18:21 +01:00
Spythere 0dc2c505db poprawki do braku pokazywania się niezapisanych scenerii 2023-12-07 16:16:06 +01:00
Spythere 188857d335 fix: rozkłady jazdy pokazują się na innych serwerach 2023-12-06 20:29:28 +01:00
Spythere 3dbbb3b4f9 Merge pull request #69 from Spythere/development
Wersja 1.19.1
2023-12-02 23:04:55 +01:00
Spythere 07a77c463b bump 1.19.1 2023-12-02 23:04:24 +01:00
Spythere 1a8e2231dd przejście z WS na komunikację http 2023-12-02 23:02:08 +01:00
Spythere f630d272ca Merge pull request #68 from Spythere/development
Wersja 1.19.0
2023-12-02 18:31:58 +01:00
Spythere a381cf806c wyróżnienie nicku donatora w widoku scenerii 2023-12-02 18:15:52 +01:00
Spythere 0023ab8cfd tłumaczenia; poprawki dropdown 2023-12-02 18:01:01 +01:00
Spythere a392940d52 tłumaczenia 2023-12-02 16:15:37 +01:00
Spythere 74984ad653 dodano wyróżnienie nicku wspierającego w Dzienniku 2023-12-02 16:11:29 +01:00
Spythere a8991434bc tłumaczenia statystyk pociągów 2023-12-02 16:00:28 +01:00
Spythere 825610b4c2 hotfix; aktualizacja paczek 2023-12-01 21:40:04 +01:00
Spythere fef3991206 bump: 1.19.0 2023-12-01 21:27:13 +01:00
Spythere a21cb31a0a ikona przy informacji offline pociągu 2023-12-01 21:26:57 +01:00
Spythere 23a954a0d1 statystyki ruchu online 2023-12-01 21:19:04 +01:00
Spythere b7bc9625b8 dodatkowe opcje darowizny 2023-12-01 16:11:56 +01:00
Spythere bc58196804 cleanup 2023-12-01 14:55:13 +01:00
Spythere 6550d6973e zmiana API do pobierania informacji o wspierających 2023-12-01 14:54:55 +01:00
Spythere d7a609a4f2 Merge pull request #67 from Spythere/development
poprawki tłumaczeń; scrolling
2023-11-28 19:12:57 +01:00
Spythere 763506d5a9 bump: 1.18.6 2023-11-28 19:10:08 +01:00
Spythere 8dbb32b821 dodatkowe tłumaczenia 2023-11-28 19:09:14 +01:00
Spythere b8a21e0f70 Merge pull request #66 from Spythere/development
hotfix: scrollowanie tabelki scenerii
2023-11-28 18:58:27 +01:00
Spythere 7c2b5fbd50 hotfix scrollowania 2023-11-28 18:56:02 +01:00
60 changed files with 12170 additions and 1914 deletions
+184 -432
View File
File diff suppressed because it is too large Load Diff
+8 -9
View File
@@ -1,6 +1,6 @@
{ {
"name": "stacjownik", "name": "stacjownik",
"version": "1.18.5", "version": "1.19.4",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
@@ -18,27 +18,26 @@
"howler": "^2.2.4", "howler": "^2.2.4",
"pinia": "^2.1.6", "pinia": "^2.1.6",
"sass": "^1.67.0", "sass": "^1.67.0",
"socket.io-client": "^4.7.2",
"vue": "^3.3.4", "vue": "^3.3.4",
"vue-i18n": "^9.4.1", "vue-i18n": "^9.4.1",
"vue-router": "^4.2.4" "vue-router": "^4.2.4"
}, },
"devDependencies": { "devDependencies": {
"@rushstack/eslint-patch": "^1.3.3",
"@types/node": "^20.6.2", "@types/node": "^20.6.2",
"@vite-pwa/assets-generator": "^0.0.10", "@vite-pwa/assets-generator": "^0.0.10",
"@vitejs/plugin-vue": "^4.3.4", "@vitejs/plugin-vue": "^4.3.4",
"@vue/eslint-config-prettier": "^8.0.0",
"@vue/eslint-config-typescript": "^12.0.0",
"@vue/tsconfig": "^0.4.0",
"axios": "^1.5.0", "axios": "^1.5.0",
"eslint": "^8.49.0",
"eslint-plugin-vue": "^9.17.0",
"prettier": "^3.0.3", "prettier": "^3.0.3",
"typescript": "^5.2.2", "typescript": "^5.2.2",
"vite": "^4.4.9", "vite": "^4.4.9",
"vite-plugin-pwa": "^0.16.5", "vite-plugin-pwa": "^0.16.5",
"vue-tsc": "^1.8.11", "vue-tsc": "^1.8.11"
"@vue/eslint-config-prettier": "^8.0.0",
"@vue/eslint-config-typescript": "^12.0.0",
"@vue/tsconfig": "^0.4.0",
"eslint": "^8.49.0",
"eslint-plugin-vue": "^9.17.0",
"@rushstack/eslint-patch": "^1.3.3"
}, },
"browserslist": [ "browserslist": [
"> 1%", "> 1%",
+56
View File
@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Warstwa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 159 38.8" style="enable-background:new 0 0 159 38.8;" xml:space="preserve">
<style type="text/css">
.st0{fill:#00A962;}
.st1{enable-background:new ;}
.st2{fill:#FFFFFF;}
.st3{fill:#1E3A33;}
.st4{fill-rule:evenodd;clip-rule:evenodd;fill:#FFFFFF;}
</style>
<path class="st0" d="M139.6,38.7H19.4C8.7,38.7,0,30.1,0,19.3l0,0C0,8.6,8.7-0.1,19.4-0.1h120.2c10.7,0,19.4,8.7,19.4,19.4l0,0
C159,30.1,150.3,38.7,139.6,38.7z"/>
<g class="st1">
<path class="st2" d="M48.9,23.7c0,0.5-0.4,0.9-0.9,0.9c-0.5,0-0.9-0.4-0.9-0.9v-9.9c0-0.6,0.4-1,1-1h3.7c2.5,0,3.6,1.9,3.6,3.7
s-1,3.7-3.6,3.7h-2.9V23.7z M48.9,14.5v4.1h2.8c1.3,0,1.9-1,1.9-2c0-1-0.5-2.1-1.8-2.1H48.9z"/>
<path class="st2" d="M56.3,20.5c0-2.5,1.9-4.1,4.1-4.1c2.3,0,4.2,1.6,4.2,4.1c0,2.5-1.9,4.2-4.2,4.2C58.2,24.7,56.3,23,56.3,20.5z
M62.9,20.5c0-1.5-1.1-2.4-2.4-2.4c-1.3,0-2.4,1-2.4,2.4c0,1.5,1.1,2.5,2.4,2.5C61.8,23,62.9,22,62.9,20.5z"/>
<path class="st2" d="M66.6,21.9c0.4-0.2,0.7,0.1,0.9,0.3c0.3,0.6,0.9,1,1.7,1c0.8,0,1.4-0.4,1.4-1c0-0.5-0.5-0.7-1.1-0.9l-1.1-0.3
c-1.7-0.5-2.3-1.4-2.2-2.7c0.1-1.2,1.4-2.1,2.8-2.1c1.1,0,2,0.3,2.5,1.2c0.2,0.4,0.1,0.8-0.2,1c-0.3,0.2-0.6,0.2-1-0.1
c-0.4-0.4-1-0.5-1.4-0.5c-0.4,0-0.9,0.2-1.1,0.4c-0.1,0.2-0.2,0.4-0.1,0.7c0.1,0.3,0.6,0.5,1,0.6l1.2,0.3c1.7,0.4,2.1,1.5,2.1,2.3
c0,1.5-1.3,2.4-3.2,2.4c-1.2,0-2.5-0.6-2.9-1.8C66,22.5,66.2,22.1,66.6,21.9z"/>
<path class="st2" d="M74.6,18h-0.8c-0.4,0-0.7-0.3-0.7-0.7c0-0.4,0.3-0.7,0.7-0.7h0.8v-2.2c0-0.5,0.4-0.9,0.9-0.9
c0.5,0,0.9,0.4,0.9,0.9v2.2h1c0.4,0,0.7,0.3,0.7,0.7c0,0.4-0.3,0.7-0.7,0.7h-1v5.7c0,0.5-0.4,0.9-0.9,0.9c-0.5,0-0.9-0.4-0.9-0.9
V18z"/>
<path class="st2" d="M85.7,23.4c-0.5,0.9-1.7,1.3-2.7,1.3c-2.1,0-4-1.6-4-4.1c0-2.5,1.9-4.1,4-4.1c1,0,2.1,0.4,2.7,1.3v-0.3
c0-0.5,0.4-0.9,0.9-0.9c0.5,0,0.9,0.4,0.9,0.9v6.3c0,0.5-0.4,0.9-0.9,0.9c-0.5,0-0.9-0.4-0.9-0.9V23.4z M83.2,23
c1.3,0,2.4-0.9,2.4-2.5c0-1.6-1.3-2.4-2.4-2.4c-1.3,0-2.4,1-2.4,2.4C80.8,22,81.9,23,83.2,23z"/>
<path class="st2" d="M95.1,16.6c0.5,0,0.9,0.3,1,0.8l1.6,4.6l1.7-4.8c0.2-0.5,0.6-0.7,1.1-0.6c0.5,0.1,0.7,0.6,0.6,1.1l-2.4,6.3
c-0.2,0.5-0.5,0.7-0.9,0.7h-0.1c-0.4,0-0.7-0.2-0.9-0.7L95.1,19l-1.7,4.9c-0.2,0.5-0.5,0.7-0.9,0.7h-0.1c-0.4,0-0.7-0.2-0.9-0.7
l-2.4-6.3c-0.2-0.4,0.1-1,0.6-1.1c0.5-0.1,0.9,0.1,1.1,0.6l1.7,4.8l1.6-4.6C94.2,16.8,94.6,16.6,95.1,16.6L95.1,16.6z"/>
<path class="st2" d="M112.9,23.2c0.3,0.3,0.3,0.9,0,1.2c-0.4,0.3-0.9,0.3-1.2,0l-3.2-3.5v2.9c0,0.5-0.4,0.9-0.9,0.9
c-0.5,0-0.8-0.4-0.8-0.9V13.1c0-0.5,0.4-0.9,0.8-0.9c0.5,0,0.9,0.4,0.9,0.9v6.1l2.4-2.4c0.3-0.3,0.9-0.3,1.2,0
c0.3,0.4,0.3,0.9,0,1.2l-2,2.1L112.9,23.2z"/>
<path class="st2" d="M120.8,23.4c-0.5,0.9-1.7,1.3-2.7,1.3c-2.1,0-4-1.6-4-4.1c0-2.5,1.9-4.1,4-4.1c1,0,2.1,0.4,2.7,1.3v-0.3
c0-0.5,0.4-0.9,0.8-0.9c0.5,0,0.9,0.4,0.9,0.9v6.3c0,0.5-0.4,0.9-0.9,0.9c-0.5,0-0.8-0.4-0.8-0.9V23.4z M118.4,23
c1.3,0,2.4-0.9,2.4-2.5c0-1.6-1.3-2.4-2.4-2.4c-1.3,0-2.4,1-2.4,2.4C115.9,22,117,23,118.4,23z"/>
<path class="st2" d="M130.3,16.6c0.5,0,0.9,0.3,1,0.8l1.6,4.6l1.7-4.8c0.2-0.5,0.6-0.7,1.1-0.6c0.5,0.1,0.7,0.6,0.6,1.1l-2.4,6.3
c-0.2,0.5-0.5,0.7-0.9,0.7h-0.1c-0.4,0-0.7-0.2-0.9-0.7l-1.7-4.9l-1.7,4.9c-0.2,0.5-0.5,0.7-0.9,0.7h-0.1c-0.4,0-0.7-0.2-0.9-0.7
l-2.4-6.3c-0.2-0.4,0.1-1,0.6-1.1c0.5-0.1,0.9,0.1,1.1,0.6l1.7,4.8l1.6-4.6C129.3,16.8,129.7,16.6,130.3,16.6L130.3,16.6z"/>
<path class="st2" d="M143.6,26.1c0,0.2,0.2,0.5,0.6,0.5c0.1,0,0.4,0,0.5-0.4c0.1-0.4,0.3-0.5,0.7-0.5c0.4,0.1,0.6,0.4,0.5,0.8
c-0.2,0.8-0.9,1.4-1.7,1.4c-1.3,0-1.9-0.8-1.9-1.7c0-0.2,0.1-0.6,0.2-0.8l0.5-1c-0.5,0.2-1,0.3-1.6,0.3c-2.3,0-4.1-1.6-4.1-4.2
c0-2.5,1.8-4.1,4.1-4.1c2.2,0,3.9,1.5,4,4c0,0.5-0.4,0.8-0.8,0.8h-5.5c0.2,1.2,1.2,2,2.4,2c0.8,0,1.3-0.3,1.9-0.8
c0.3-0.2,0.8-0.4,1.1-0.1c0.3,0.3,0.2,0.7,0,1.1C143.5,25.9,143.6,26.1,143.6,26.1z M143.7,19.9c-0.2-1.2-1.1-2-2.3-2
c-1.2,0-2.2,0.7-2.3,2H143.7z"/>
</g>
<g>
<path class="st3" d="M21.6,26.7c-4.9,0-7.4-2.6-8.5-4.9c-1.2-2.4-1.4-5-1.4-6c0-2.2,1.8-3.8,4.3-3.8h7c0.7,0,1.3,0.6,1.3,1.3
c0,0.7-0.6,1.3-1.3,1.3h-7c-0.5,0-1,0.1-1.3,0.4c-0.3,0.2-0.4,0.4-0.4,0.8c0,1.9,0.5,8.3,7.3,8.3c5.8,0,7.3-4.6,7.9-8.1
c0-0.2,0-0.3,0.1-0.4c0.2-0.9,0.4-1.7,0.8-2.3C31,12.4,31.9,12,33,12c0.5,0,0.9,0.2,1.2,0.5c0.5,0.4,0.9,1.3,1.2,4.1
c0.1,1.4,0.1,2.6,0.1,2.7c0,0.7-0.6,1.3-1.3,1.3l0,0c-0.7,0-1.3-0.6-1.3-1.3c0,0,0-1.1-0.1-2.3c-0.1-1-0.2-1.7-0.3-2.1
c-0.1,0.3-0.2,0.9-0.3,1.1c0,0.1,0,0.2-0.1,0.4c-0.2,1.2-0.7,3.7-2.1,5.9c-0.8,1.3-1.9,2.3-3.2,3.1C25.4,26.3,23.6,26.7,21.6,26.7z
"/>
<ellipse class="st4" cx="26.7" cy="13.3" rx="1.3" ry="1.3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

+7 -8
View File
@@ -39,12 +39,13 @@ import Clock from './components/App/Clock.vue';
import packageInfo from '.././package.json'; import packageInfo from '.././package.json';
import { regions } from './data/options.json'; import { regions } from './data/options.json';
import { useStore } from './store/mainStore'; import { useMainStore } from './store/mainStore';
import StatusIndicator from './components/App/StatusIndicator.vue'; import StatusIndicator from './components/App/StatusIndicator.vue';
import TrainModal from './components/Global/TrainModal.vue'; import TrainModal from './components/Global/TrainModal.vue';
import AppHeader from './components/App/AppHeader.vue'; import AppHeader from './components/App/AppHeader.vue';
import axios from 'axios'; import axios from 'axios';
import StorageManager from './managers/storageManager'; import StorageManager from './managers/storageManager';
import { useApiStore } from './store/apiStore';
export default defineComponent({ export default defineComponent({
components: { components: {
@@ -56,7 +57,8 @@ export default defineComponent({
data: () => ({ data: () => ({
VERSION: packageInfo.version, VERSION: packageInfo.version,
store: useStore(), store: useMainStore(),
apiStore: useApiStore(),
currentLang: 'pl', currentLang: 'pl',
releaseURL: '', releaseURL: '',
@@ -65,18 +67,15 @@ export default defineComponent({
created() { created() {
this.loadLang(); this.loadLang();
this.store.connectToAPI(); this.apiStore.setupAPI();
this.store.isOffline = !window.navigator.onLine; this.store.isOffline = !window.navigator.onLine;
window.addEventListener('offline', () => { window.addEventListener('offline', () => {
this.store.isOffline = true; this.store.isOffline = true;
this.apiStore.activeData = undefined;
this.store.activeData.activeSceneries = []; this.apiStore.setDataStatuses();
this.store.activeData.trains = [];
this.store.activeData.connectedSocketCount = 0;
this.store.setStatuses();
}); });
window.addEventListener('online', () => { window.addEventListener('online', () => {
+2 -2
View File
@@ -68,7 +68,7 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { useStore } from '../../store/mainStore'; import { useMainStore } from '../../store/mainStore';
import StatusIndicator from './StatusIndicator.vue'; import StatusIndicator from './StatusIndicator.vue';
import Clock from './Clock.vue'; import Clock from './Clock.vue';
import RegionDropdown from '../Global/RegionDropdown.vue'; import RegionDropdown from '../Global/RegionDropdown.vue';
@@ -84,7 +84,7 @@ export default defineComponent({
setup() { setup() {
return { return {
store: useStore() store: useMainStore()
}; };
}, },
+6 -5
View File
@@ -194,9 +194,9 @@
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { StoreState } from '../../store/typings'; import { useMainStore } from '../../store/mainStore';
import { useStore } from '../../store/mainStore';
import { Status } from '../../typings/common'; import { Status } from '../../typings/common';
import { useApiStore } from '../../store/apiStore';
export default defineComponent({ export default defineComponent({
data() { data() {
@@ -221,10 +221,11 @@ export default defineComponent({
}, },
setup() { setup() {
const store = useStore(); const store = useMainStore();
const apiStore = useApiStore();
return { return {
dataStatus: store.dataStatuses, dataStatus: apiStore.dataStatuses,
store store
}; };
}, },
@@ -233,7 +234,7 @@ export default defineComponent({
dataStatus: { dataStatus: {
deep: true, deep: true,
handler(statuses: StoreState['dataStatuses']) { handler(statuses: any) {
const connectionStatus = statuses.connection; const connectionStatus = statuses.connection;
const sceneryDataStatus = statuses.sceneries; const sceneryDataStatus = statuses.sceneries;
const trainsDataStatus = statuses.trains; const trainsDataStatus = statuses.trains;
+2 -2
View File
@@ -12,7 +12,7 @@
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { useStore } from '../../store/mainStore'; import { useMainStore } from '../../store/mainStore';
export default defineComponent({ export default defineComponent({
emits: ['toggleModal'], emits: ['toggleModal'],
@@ -23,7 +23,7 @@ export default defineComponent({
data() { data() {
return { return {
store: useStore() store: useMainStore()
}; };
}, },
+58 -41
View File
@@ -1,7 +1,7 @@
<template> <template>
<div class="donation-modal" @keydown.esc="toggleModal(false)"> <div class="donation-modal" @keydown.esc="toggleModal(false)">
<button <button
class="modal_button action btn--image" class="btn-toggle btn--image"
ref="btn" ref="btn"
@click="toggleModal(true)" @click="toggleModal(true)"
@focus="toggleModal(false)" @focus="toggleModal(false)"
@@ -28,7 +28,7 @@
<b>{{ $t('donations.p2-b3') }}</b> <b>{{ $t('donations.p2-b3') }}</b>
</template> </template>
<template v-slot:link> <template v-slot:link>
<a href="https://discord.gg/x2mpNN3svk" target="_blank"> <a class="discord" href="https://discord.gg/x2mpNN3svk" target="_blank">
{{ $t('donations.p2-a1') }} {{ $t('donations.p2-a1') }}
</a> </a>
</template> </template>
@@ -58,19 +58,28 @@
</div> </div>
<div class="modal_actions"> <div class="modal_actions">
<button class="modal_button exit btn--image" @click="toggleModal(false)"> <a
class="modal-action a-button btn--image coffee"
href="https://buycoffee.to/spythere"
target="_blank"
>
<img src="/images/icon-coffee.png" width="20" alt="buycoffee.to donation" />
{{ $t('donations.action-buycoffee') }}
</a>
<a
class="modal-action a-button btn--image paypal"
href="https://www.paypal.com/donate/?hosted_button_id=EDB3SKFAHXFTW"
target="_blank"
>
<img src="/images/icon-dollar.svg" alt="paypal donation" />
{{ $t('donations.action-paypal') }}
</a>
<button class="modal-action btn--image exit" @click="toggleModal(false)">
<img src="/images/icon-exit.svg" alt="dollar donation icon" /> <img src="/images/icon-exit.svg" alt="dollar donation icon" />
{{ $t('donations.action-exit') }} {{ $t('donations.action-exit') }}
</button> </button>
<form action="https://www.paypal.com/donate" method="post">
<input type="hidden" name="hosted_button_id" value="EDB3SKFAHXFTW" />
<button class="modal_button action btn--image" @click="toggleModal(false)">
<img src="/images/icon-dollar.svg" alt="dollar donation icon" />
{{ $t('donations.action-confirm') }}
</button>
</form>
</div> </div>
</div> </div>
</AnimatedModal> </AnimatedModal>
@@ -99,25 +108,13 @@ export default defineComponent({
<style lang="scss" scoped> <style lang="scss" scoped>
@import '../../styles/responsive.scss'; @import '../../styles/responsive.scss';
.modal_button { button.btn-toggle {
&.action { $btnColor: #254069;
$btnColor: #254069;
background-color: $btnColor; background-color: $btnColor;
&:hover { &:hover {
background-color: lighten($btnColor, 10%); background-color: lighten($btnColor, 5%);
}
}
&.exit {
$btnColor: crimson;
background-color: $btnColor;
&:hover {
background-color: lighten($btnColor, 10%);
}
} }
@include smallScreen { @include smallScreen {
@@ -127,16 +124,6 @@ export default defineComponent({
} }
} }
.modal-logo {
position: absolute;
top: 0;
left: 50%;
width: 6em;
transform: translate(-50%, -50%);
}
.modal_content { .modal_content {
display: grid; display: grid;
grid-template-rows: 1fr auto; grid-template-rows: 1fr auto;
@@ -157,7 +144,7 @@ export default defineComponent({
text-align: justify; text-align: justify;
} }
a { a.discord {
text-decoration: underline; text-decoration: underline;
} }
} }
@@ -174,11 +161,41 @@ export default defineComponent({
.modal_actions { .modal_actions {
display: grid; display: grid;
grid-template-columns: repeat(auto-fit, minmax(13em, 1fr)); grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 0.5em; gap: 0.5em;
form button { form button {
width: 100%; width: 100%;
} }
} }
.modal_actions > .modal-action {
&.paypal {
$btnColor: #254069;
background-color: $btnColor;
&:hover {
background-color: lighten($btnColor, 5%);
}
}
&.coffee {
$btnColor: #009255;
background-color: $btnColor;
&:hover {
background-color: lighten($btnColor, 5%);
}
}
&.exit {
$btnColor: #686868;
background-color: $btnColor;
&:hover {
background-color: lighten($btnColor, 5%);
}
}
}
</style> </style>
+2 -2
View File
@@ -30,7 +30,7 @@
<script lang="ts"> <script lang="ts">
import { defineComponent, Ref, ref } from 'vue'; import { defineComponent, Ref, ref } from 'vue';
import { regions as regionsJSON } from '../../data/options.json'; import { regions as regionsJSON } from '../../data/options.json';
import { useStore } from '../../store/mainStore'; import { useMainStore } from '../../store/mainStore';
interface Item { interface Item {
id: string; id: string;
@@ -41,7 +41,7 @@ interface Item {
export default defineComponent({ export default defineComponent({
data() { data() {
return { return {
store: useStore(), store: useMainStore(),
selectedItemIndex: 0, selectedItemIndex: 0,
listOpen: false listOpen: false
}; };
+4 -4
View File
@@ -50,8 +50,8 @@
<script lang="ts"> <script lang="ts">
import { PropType, defineComponent } from 'vue'; import { PropType, defineComponent } from 'vue';
import { useStore } from '../../store/mainStore';
import { API } from '../../typings/api'; import { API } from '../../typings/api';
import { useApiStore } from '../../store/apiStore';
export default defineComponent({ export default defineComponent({
props: { props: {
@@ -63,15 +63,15 @@ export default defineComponent({
data() { data() {
return { return {
store: useStore() apiStore: useApiStore()
}; };
}, },
methods: { methods: {
onImageError(event: Event, stockName: string) { onImageError(event: Event, stockName: string) {
const fallbackName = const fallbackName =
Object.keys(this.store.rollingStockData!.info).find((type) => { Object.keys(this.apiStore.rollingStockData!.info).find((type) => {
return this.store.rollingStockData!.info[type as keyof API.RollingStock.Info].find( return this.apiStore.rollingStockData!.info[type as keyof API.RollingStock.Info].find(
(v) => v[0] === stockName.split(':')[0] (v) => v[0] === stockName.split(':')[0]
); );
}) || 'vehicle-unknown'; }) || 'vehicle-unknown';
+5 -5
View File
@@ -16,8 +16,8 @@
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { useStore } from '../../store/mainStore';
import { API } from '../../typings/api'; import { API } from '../../typings/api';
import { useApiStore } from '../../store/apiStore';
export default defineComponent({ export default defineComponent({
props: { props: {
@@ -34,7 +34,7 @@ export default defineComponent({
data() { data() {
return { return {
store: useStore(), apiStore: useApiStore(),
isNotFound: false, isNotFound: false,
isLoaded: false isLoaded: false
}; };
@@ -50,11 +50,11 @@ export default defineComponent({
}, },
stockType() { stockType() {
if (!this.store.rollingStockData) return 'vehicle-unknown'; if (!this.apiStore.rollingStockData) return 'vehicle-unknown';
return ( return (
Object.keys(this.store.rollingStockData.info).find((type) => { Object.keys(this.apiStore.rollingStockData.info).find((type) => {
return this.store.rollingStockData?.info[type as keyof API.RollingStock.Info].find( return this.apiStore.rollingStockData?.info[type as keyof API.RollingStock.Info].find(
(v) => v[0] === this.name.split(':')[0] (v) => v[0] === this.name.split(':')[0]
); );
}) || 'vehicle-unknown' }) || 'vehicle-unknown'
+5 -10
View File
@@ -50,18 +50,17 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import axios from 'axios';
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { URLs } from '../../scripts/utils/apiURLs'; import { useMainStore } from '../../store/mainStore';
import { useStore } from '../../store/mainStore';
import Loading from '../Global/Loading.vue'; import Loading from '../Global/Loading.vue';
import { API } from '../../typings/api'; import { API } from '../../typings/api';
import http from '../../http';
export default defineComponent({ export default defineComponent({
components: { Loading }, components: { Loading },
setup() { setup() {
const store = useStore(); const store = useMainStore();
return { return {
store store
@@ -90,15 +89,11 @@ export default defineComponent({
} }
const statsData: API.DispatcherStats.Response = await ( const statsData: API.DispatcherStats.Response = await (
await axios.get( await http.get('api/getDispatcherInfo?name=${this.store.dispatcherStatsName}')
`${URLs.stacjownikAPI}/api/getDispatcherInfo?name=${this.store.dispatcherStatsName}`
)
).data; ).data;
const timetables: API.TimetableHistory.Response = await ( const timetables: API.TimetableHistory.Response = await (
await axios.get( await http.get('api/getTimetables?authorName=${this.store.dispatcherStatsName}')
`${URLs.stacjownikAPI}/api/getTimetables?authorName=${this.store.dispatcherStatsName}`
)
).data; ).data;
this.timetables = timetables; this.timetables = timetables;
@@ -5,15 +5,12 @@
{{ $t('app.loading') }} {{ $t('app.loading') }}
</b> </b>
<b v-else-if="stats.distanceSum == null">
{{ $t('journal.daily-stats-info') }}
</b>
<span class="stats-list" v-else> <span class="stats-list" v-else>
<h3> <h3>
{{ $t('journal.daily-stats-title') }} {{ $t('journal.daily-stats-title') }}
<b class="text--primary">{{ new Date().toLocaleDateString($i18n.locale) }}</b> <b class="text--primary">{{ new Date().toLocaleDateString($i18n.locale) }}</b>
</h3> </h3>
<hr style="margin-bottom: 0.5em" /> <hr style="margin-bottom: 0.5em" />
<div v-if="stats.totalTimetables"> <div v-if="stats.totalTimetables">
@@ -133,17 +130,18 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import axios from 'axios';
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import dateMixin from '../../mixins/dateMixin'; import dateMixin from '../../mixins/dateMixin';
import { URLs } from '../../scripts/utils/apiURLs';
import { API } from '../../typings/api'; import { API } from '../../typings/api';
import { Status } from '../../typings/common'; import { Status } from '../../typings/common';
import http from '../../http';
export default defineComponent({ export default defineComponent({
name: 'journal-daily-stats',
mixins: [dateMixin], mixins: [dateMixin],
emits: ['toggleStatsOpen'], // emits: ['toggleStatsOpen'],
data() { data() {
return { return {
@@ -157,7 +155,7 @@ export default defineComponent({
activated() { activated() {
this.startFetchingDailyStats(); this.startFetchingDailyStats();
this.$emit('toggleStatsOpen', true); // this.$emit('toggleStatsOpen', true);
}, },
deactivated() { deactivated() {
@@ -176,23 +174,7 @@ export default defineComponent({
methods: { methods: {
async fetchDailyTimetableStats() { async fetchDailyTimetableStats() {
try { try {
const res: API.DailyStats.Response = await ( const res: API.DailyStats.Response = await (await http.get('api/getDailyStats')).data;
await axios.get(`${URLs.stacjownikAPI}/api/getDailyTimetableStats`)
).data;
// this.stats = {
// totalTimetables: res.totalTimetables,
// distanceSum: res.distanceSum,
// distanceAvg: res.distanceAvg,
// // timetableAuthor: res.maxTimetable?.authorName || '',
// // timetableDriver: res.maxTimetable?.driverName || '',
// // timetableId: res.maxTimetable?.id || 0,
// // timetableRouteDistance: res.maxTimetable?.routeDistance || 0,
// mostActiveDispatchers: res.mostActiveDispatchers,
// mostActiveDrivers: res.mostActiveDrivers,
// longestDuties: res.longestDuties
// };
this.stats = res; this.stats = res;
@@ -208,7 +190,7 @@ export default defineComponent({
if (this.intervalId != -1) return; if (this.intervalId != -1) return;
this.intervalId = setInterval(this.fetchDailyTimetableStats, 60000); this.intervalId = window.setInterval(this.fetchDailyTimetableStats, 60000);
}, },
stopFetchingDailyStats() { stopFetchingDailyStats() {
@@ -43,7 +43,17 @@
<router-link <router-link
:to="`/journal/dispatchers?dispatcherName=${historyItem.dispatcherName}`" :to="`/journal/dispatchers?dispatcherName=${historyItem.dispatcherName}`"
> >
<b>{{ historyItem.dispatcherName }}</b> <b
v-if="isDonator(historyItem.dispatcherName)"
class="text--donator"
:title="$t('donations.dispatcher-message')"
>
{{ historyItem.dispatcherName }}
</b>
<b v-else>
{{ historyItem.dispatcherName }}
</b>
</router-link> </router-link>
</td> </td>
<td> <td>
@@ -115,17 +125,18 @@
import { defineComponent, PropType } from 'vue'; import { defineComponent, PropType } from 'vue';
import dateMixin from '../../mixins/dateMixin'; import dateMixin from '../../mixins/dateMixin';
import styleMixin from '../../mixins/styleMixin'; import styleMixin from '../../mixins/styleMixin';
import { useStore } from '../../store/mainStore'; import { useMainStore } from '../../store/mainStore';
import Loading from '../Global/Loading.vue'; import Loading from '../Global/Loading.vue';
import { regions } from '../../data/options.json'; import { regions } from '../../data/options.json';
import AddDataButton from '../Global/AddDataButton.vue'; import AddDataButton from '../Global/AddDataButton.vue';
import { API } from '../../typings/api'; import { API } from '../../typings/api';
import { Status } from '../../typings/common'; import { Status } from '../../typings/common';
import donatorMixin from '../../mixins/donatorMixin';
export default defineComponent({ export default defineComponent({
components: { Loading, AddDataButton }, components: { Loading, AddDataButton },
mixins: [dateMixin, styleMixin], mixins: [dateMixin, styleMixin, donatorMixin],
props: { props: {
dispatcherHistory: { dispatcherHistory: {
@@ -149,7 +160,7 @@ export default defineComponent({
data() { data() {
return { return {
Status, Status,
store: useStore(), store: useMainStore(),
regions regions
}; };
}, },
@@ -55,13 +55,15 @@
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { useStore } from '../../store/mainStore'; import { useMainStore } from '../../store/mainStore';
import { Status } from '../../typings/common'; import { Status } from '../../typings/common';
export default defineComponent({ export default defineComponent({
name: 'journal-driver-stats',
data() { data() {
return { return {
store: useStore(), store: useMainStore(),
Status: Status Status: Status
}; };
} }
+12 -16
View File
@@ -1,6 +1,6 @@
<template> <template>
<div class="filters-options" @keydown.esc="showOptions = false"> <div class="filters-options dropdown" @keydown.esc="showOptions = false">
<div class="bg" v-if="showOptions" @click="showOptions = false"></div> <div class="dropdown_background" v-if="showOptions" @click="showOptions = false"></div>
<div class="actions-bar"> <div class="actions-bar">
<button <button
@@ -27,8 +27,8 @@
<option v-for="(sugg, i) in dispatcherSuggestions" :key="i" :value="sugg"></option> <option v-for="(sugg, i) in dispatcherSuggestions" :key="i" :value="sugg"></option>
</datalist> </datalist>
<transition name="options-anim"> <transition name="dropdown-anim">
<div class="options_wrapper" v-if="showOptions"> <div class="dropdown_wrapper" v-if="showOptions">
<div class="options_content"> <div class="options_content">
<h1 class="option-title">{{ $t('options.search-title') }}</h1> <h1 class="option-title">{{ $t('options.search-title') }}</h1>
<div class="search_content"> <div class="search_content">
@@ -110,14 +110,13 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import axios from 'axios';
import { defineComponent, inject, PropType } from 'vue'; import { defineComponent, inject, PropType } from 'vue';
import keyMixin from '../../mixins/keyMixin'; import keyMixin from '../../mixins/keyMixin';
import { URLs } from '../../scripts/utils/apiURLs'; import { useMainStore } from '../../store/mainStore';
import { useStore } from '../../store/mainStore';
import { Journal } from './typings'; import { Journal } from './typings';
import { API } from '../../typings/api'; import { API } from '../../typings/api';
import { Status } from '../../typings/common'; import { Status } from '../../typings/common';
import http from '../../http';
export default defineComponent({ export default defineComponent({
emits: ['onSearchConfirm', 'onOptionsReset', 'onRefreshData'], emits: ['onSearchConfirm', 'onOptionsReset', 'onRefreshData'],
@@ -158,7 +157,7 @@ export default defineComponent({
dispatcherSuggestions: [] as string[], dispatcherSuggestions: [] as string[],
searchTimeout: 0, searchTimeout: 0,
store: useStore(), store: useMainStore(),
JournalFilterSection: Journal.FilterSection JournalFilterSection: Journal.FilterSection
}; };
@@ -184,8 +183,6 @@ export default defineComponent({
watch: { watch: {
async 'store.driverStatsName'() { async 'store.driverStatsName'() {
await this.fetchDriverStats(); await this.fetchDriverStats();
// if (value) this.store.currentStatsTab = 'driver';
}, },
async 'searchersValues.search-driver'(value: string | undefined) { async 'searchersValues.search-driver'(value: string | undefined) {
@@ -218,9 +215,7 @@ export default defineComponent({
this.store.driverStatsStatus = Status.Data.Loading; this.store.driverStatsStatus = Status.Data.Loading;
const statsData: API.DriverStats.Response = await ( const statsData: API.DriverStats.Response = await (
await axios.get( await http.get(`api/getDriverInfo?name=${this.store.driverStatsName}`)
`${URLs.stacjownikAPI}/api/getDriverInfo?name=${this.store.driverStatsName}`
)
).data; ).data;
this.store.driverStatsData = statsData; this.store.driverStatsData = statsData;
@@ -240,10 +235,10 @@ export default defineComponent({
window.clearTimeout(this.searchTimeout); window.clearTimeout(this.searchTimeout);
this.searchTimeout = setTimeout(async () => { this.searchTimeout = window.setTimeout(async () => {
try { try {
const suggestions: string[] = await ( const suggestions: string[] = await (
await axios.get(`${URLs.stacjownikAPI}/api/get${type}Suggestions?name=${value}`) await http.get(`api/get${type}Suggestions?name=${value}`)
).data; ).data;
this[`${type}Suggestions`] = suggestions; this[`${type}Suggestions`] = suggestions;
@@ -300,5 +295,6 @@ export default defineComponent({
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import '../../styles/filters_options.scss'; @import '../../styles/dropdown.scss';
@import '../../styles/dropdown_filters.scss';
</style> </style>
+40 -59
View File
@@ -1,27 +1,26 @@
<template> <template>
<div class="journal-stats" v-if="!store.isOffline"> <div class="journal-stats" v-if="!store.isOffline">
<div class="tabs"> <div class="stats-buttons">
<button <button
v-for="tab in data.tabs" v-for="button in data.statsButtons"
:key="tab.name" :key="button.name"
class="btn--filled" class="btn--filled btn--image"
:data-selected="tab.name == store.currentStatsTab && areStatsOpen" :data-selected="button.name == currentStatsTab"
:data-inactive="tab.inactive" @click="onTabButtonClick(button.name)"
:data-disabled="tab.inactive"
:disabled="tab.inactive"
@click="onTabButtonClick(tab.name)"
> >
{{ $t(tab.titlePath) }} <img
v-if="button.iconName"
:src="`/images/icon-${button.iconName}.svg`"
:alt="button.iconName"
/>
{{ $t(button.localeKey) }}
</button> </button>
</div> </div>
<div class="stats-tab" v-show="areStatsOpen"> <div class="stats-tab" v-show="currentStatsTab !== null">
<keep-alive> <keep-alive>
<JournalDailyStats <JournalDailyStats v-if="currentStatsTab == 'journal-daily-stats'" />
v-if="store.currentStatsTab == 'daily'" <JournalDriverStats v-else-if="currentStatsTab == 'journal-driver-stats'" />
@toggleStatsOpen="toggleStatsOpen"
/>
<JournalDriverStats v-else-if="store.currentStatsTab == 'driver'" />
</keep-alive> </keep-alive>
</div> </div>
</div> </div>
@@ -29,68 +28,50 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, onMounted, reactive, Ref, ref, watch } from 'vue'; import { computed, onMounted, reactive, Ref, ref, watch } from 'vue';
import { useStore } from '../../store/mainStore'; import { useMainStore } from '../../store/mainStore';
import JournalDailyStats from './DailyStats.vue'; import JournalDailyStats from './JournalDailyStats.vue';
import JournalDriverStats from './JournalDriverStats.vue'; import JournalDriverStats from './JournalDriverStats.vue';
import StorageManager from '../../managers/storageManager'; import StorageManager from '../../managers/storageManager';
// Types export type JournalStatsTab = 'journal-driver-stats' | 'journal-daily-stats';
type TStatTab = 'daily' | 'driver';
// Variables const store = useMainStore();
const store = useStore(); const currentStatsTab: Ref<JournalStatsTab | null> = ref(null);
const lastDailyStatsOpen = ref(false);
const areStatsOpen = ref(false);
const lastClickedTab: Ref<'daily' | 'driver' | null> = ref(null);
let data = reactive({ let data = reactive({
tabs: [ statsButtons: [
{ {
name: 'daily', name: 'journal-daily-stats',
titlePath: 'journal.daily-stats-title' localeKey: 'journal.daily-stats-title',
iconName: 'stats'
}, },
{ {
name: 'driver', name: 'journal-driver-stats',
titlePath: 'journal.driver-stats-title' localeKey: 'journal.driver-stats-title',
// inactive: true, iconName: 'user'
} }
] as { name: TStatTab; titlePath: string; inactive?: boolean }[] ] as { name: JournalStatsTab; localeKey: string; iconName?: string }[]
}); });
// Methods function onTabButtonClick(tab: JournalStatsTab) {
function onTabButtonClick(tab: TStatTab) { currentStatsTab.value = tab == currentStatsTab.value ? null : tab;
if (lastClickedTab.value == tab || !lastClickedTab.value || !areStatsOpen.value) StorageManager.setStringValue('journalStatsTab', currentStatsTab.value ?? '');
areStatsOpen.value = !areStatsOpen.value;
if (tab == 'daily') {
StorageManager.setBooleanValue('dailyStatsOpen', areStatsOpen.value);
lastDailyStatsOpen.value = areStatsOpen.value;
}
store.currentStatsTab = tab;
lastClickedTab.value = tab;
if (areStatsOpen.value == false) store.currentStatsTab = null;
}
function toggleStatsOpen(open: boolean) {
areStatsOpen.value = open;
} }
watch( watch(
computed(() => store.driverStatsData), computed(() => store.driverStatsData),
(statsData) => { (newData, prevData) => {
store.currentStatsTab = statsData ? 'driver' : lastClickedTab.value; currentStatsTab.value =
areStatsOpen.value = statsData ? true : lastClickedTab.value !== null; JSON.stringify(prevData) !== JSON.stringify(newData) && newData !== undefined
? 'journal-driver-stats'
: currentStatsTab.value;
} }
); );
onMounted(() => { onMounted(() => {
if (StorageManager.getBooleanValue('dailyStatsOpen')) { const storedTab = StorageManager.getStringValue('journalStatsTab');
areStatsOpen.value = true;
store.currentStatsTab = 'daily'; if (storedTab && storedTab !== '') currentStatsTab.value = storedTab as JournalStatsTab;
}
}); });
</script> </script>
@@ -98,7 +79,7 @@ onMounted(() => {
@import '../../styles/JournalStats.scss'; @import '../../styles/JournalStats.scss';
@import '../../styles/variables.scss'; @import '../../styles/variables.scss';
.tabs { .stats-buttons {
position: relative; position: relative;
display: flex; display: flex;
@@ -42,7 +42,7 @@ import { defineComponent, PropType } from 'vue';
import Loading from '../../Global/Loading.vue'; import Loading from '../../Global/Loading.vue';
import AddDataButton from '../../Global/AddDataButton.vue'; import AddDataButton from '../../Global/AddDataButton.vue';
import TimetableHistoryList from './TimetableHistoryList.vue'; import TimetableHistoryList from './TimetableHistoryList.vue';
import { useStore } from '../../../store/mainStore'; import { useMainStore } from '../../../store/mainStore';
import { Status } from '../../../typings/common'; import { Status } from '../../../typings/common';
import { API } from '../../../typings/api'; import { API } from '../../../typings/api';
@@ -71,7 +71,7 @@ export default defineComponent({
data() { data() {
return { return {
Status, Status,
store: useStore() store: useMainStore()
}; };
} }
}); });
@@ -28,7 +28,17 @@
{{ timetable.driverLevel < 2 ? 'L' : `${timetable.driverLevel}` }} {{ timetable.driverLevel < 2 ? 'L' : `${timetable.driverLevel}` }}
</strong> </strong>
<strong>{{ timetable.driverName }}</strong> <strong
v-if="isDonator(timetable.driverName)"
class="text--donator"
:title="$t('donations.driver-message')"
>
{{ timetable.driverName }}
</strong>
<strong v-else>
{{ timetable.driverName }}
</strong>
</span> </span>
<span class="general-time"> <span class="general-time">
@@ -63,13 +73,14 @@
<script lang="ts"> <script lang="ts">
import { PropType, defineComponent } from 'vue'; import { PropType, defineComponent } from 'vue';
import { API } from '../../../typings/api';
import dateMixin from '../../../mixins/dateMixin'; import dateMixin from '../../../mixins/dateMixin';
import modalTrainMixin from '../../../mixins/modalTrainMixin'; import modalTrainMixin from '../../../mixins/modalTrainMixin';
import styleMixin from '../../../mixins/styleMixin'; import styleMixin from '../../../mixins/styleMixin';
import { API } from '../../../typings/api'; import donatorMixin from '../../../mixins/donatorMixin';
export default defineComponent({ export default defineComponent({
mixins: [dateMixin, modalTrainMixin, styleMixin], mixins: [dateMixin, modalTrainMixin, styleMixin, donatorMixin],
props: { props: {
timetable: { timetable: {
@@ -100,16 +111,17 @@ export default defineComponent({
gap: 0.5em; gap: 0.5em;
margin-bottom: 0.5em; margin-bottom: 0.5em;
@include smallScreen() {
justify-content: center;
}
} }
.info-date { .info-date {
margin-right: 0.5em; margin-right: 0.5em;
} }
.badges {
display: flex;
gap: 0.25em;
}
.info-badge { .info-badge {
padding: 0.05em 0.35em; padding: 0.05em 0.35em;
color: black; color: black;
@@ -131,7 +143,14 @@ export default defineComponent({
cursor: pointer; cursor: pointer;
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
justify-content: center;
align-items: center; align-items: center;
gap: 0.25em; gap: 0.25em;
} }
@include smallScreen {
.item-general {
justify-content: center;
}
}
</style> </style>
@@ -46,6 +46,8 @@ import TimetableExtra from './TimetableExtra.vue';
import { API } from '../../../typings/api'; import { API } from '../../../typings/api';
export default defineComponent({ export default defineComponent({
components: { TimetableGeneral, TimetableStops, TimetableStatus, TimetableExtra },
props: { props: {
timetableHistory: { timetableHistory: {
type: Array as PropType<API.TimetableHistory.Response>, type: Array as PropType<API.TimetableHistory.Response>,
@@ -59,9 +61,7 @@ export default defineComponent({
showExtraInfo: ref(false) showExtraInfo: ref(false)
})); }));
} }
}, }
methods: {},
components: { TimetableGeneral, TimetableStops, TimetableStatus, TimetableExtra }
}); });
</script> </script>
@@ -33,6 +33,8 @@
> >
{{ historyItem.dispatcherLevel >= 2 ? historyItem.dispatcherLevel : 'L' }} {{ historyItem.dispatcherLevel >= 2 ? historyItem.dispatcherLevel : 'L' }}
</b> </b>
<b v-else>?</b>
</td> </td>
<td class="text--primary"> <td class="text--primary">
<b>{{ historyItem.dispatcherRate }}</b> <b>{{ historyItem.dispatcherRate }}</b>
@@ -66,17 +68,16 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import axios from 'axios';
import { defineComponent, PropType } from 'vue'; import { defineComponent, PropType } from 'vue';
import dateMixin from '../../mixins/dateMixin'; import dateMixin from '../../mixins/dateMixin';
import Station from '../../scripts/interfaces/Station'; import Station from '../../scripts/interfaces/Station';
import { URLs } from '../../scripts/utils/apiURLs';
import Loading from '../Global/Loading.vue'; import Loading from '../Global/Loading.vue';
import styleMixin from '../../mixins/styleMixin'; import styleMixin from '../../mixins/styleMixin';
import listObserverMixin from '../../mixins/listObserverMixin'; import listObserverMixin from '../../mixins/listObserverMixin';
import { OnlineScenery } from '../../store/typings'; import { OnlineScenery } from '../../store/typings';
import { API } from '../../typings/api'; import { API } from '../../typings/api';
import { Status } from '../../typings/common'; import { Status } from '../../typings/common';
import http from '../../http';
export default defineComponent({ export default defineComponent({
name: 'SceneryDispatchersHistory', name: 'SceneryDispatchersHistory',
@@ -84,12 +85,10 @@ export default defineComponent({
components: { Loading }, components: { Loading },
props: { props: {
station: { station: {
type: Object as PropType<Station>, type: Object as PropType<Station>
required: true
}, },
onlineScenery: { onlineScenery: {
type: Object as PropType<OnlineScenery>, type: Object as PropType<OnlineScenery>
required: false
} }
}, },
@@ -113,12 +112,20 @@ export default defineComponent({
countFrom = 0, countFrom = 0,
countLimit = 30 countLimit = 30
): Promise<API.DispatcherHistory.Response | null> { ): Promise<API.DispatcherHistory.Response | null> {
if (!this.station && !this.onlineScenery) {
this.dataStatus = Status.Data.Loaded;
return null;
}
try { try {
this.dataStatus = Status.Data.Loading; this.dataStatus = Status.Data.Loading;
const requestString = `${URLs.stacjownikAPI}/api/getDispatchers?stationName=${this.station.name}&countFrom=${countFrom}&countLimit=${countLimit}`; const requestString = `api/getDispatchers?stationName=${
this.station?.name || this.onlineScenery?.name
}&countFrom=${countFrom}&countLimit=${countLimit}`;
const historyAPIData: API.DispatcherHistory.Response = await ( const historyAPIData: API.DispatcherHistory.Response = await (
await axios.get(requestString) await http.get(requestString)
).data; ).data;
this.dataStatus = Status.Data.Loaded; this.dataStatus = Status.Data.Loaded;
@@ -130,7 +137,9 @@ export default defineComponent({
} }
}, },
navigateToHistory() { navigateToHistory() {
this.$router.push(`/journal/dispatchers?sceneryName=${this.station.name}`); this.$router.push(
`/journal/dispatchers?sceneryName=${this.station?.name || this.onlineScenery?.name}`
);
} }
} }
}); });
+10 -8
View File
@@ -1,11 +1,11 @@
<template> <template>
<section class="info-header"> <section class="info-header">
<a class="scenery-name" :href="station.generalInfo?.url" target="_blank"> <a class="scenery-name" :href="station?.generalInfo?.url" target="_blank">
{{ station.name }} {{ stationName.replace(/_/g, ' ') }}
</a> </a>
<div class="scenery-abbrev"> <div class="scenery-abbrev" v-if="station?.generalInfo?.abbr">
{{ $t('scenery.abbrev') }} <b>{{ station.generalInfo?.abbr }}</b> {{ $t('scenery.abbrev') }} <b>{{ station.generalInfo.abbr }}</b>
</div> </div>
<div class="scenery-hash" v-if="onlineScenery?.hash">#{{ onlineScenery.hash }}</div> <div class="scenery-hash" v-if="onlineScenery?.hash">#{{ onlineScenery.hash }}</div>
@@ -20,13 +20,16 @@ import { OnlineScenery } from '../../store/typings';
export default defineComponent({ export default defineComponent({
props: { props: {
station: { station: {
type: Object as PropType<Station>, type: Object as PropType<Station>
},
stationName: {
type: String,
required: true required: true
}, },
onlineScenery: { onlineScenery: {
type: Object as PropType<OnlineScenery>, type: Object as PropType<OnlineScenery>
required: false
} }
} }
}); });
@@ -58,4 +61,3 @@ export default defineComponent({
font-size: 1.2em; font-size: 1.2em;
} }
</style> </style>
../../store/storeTypes
+6 -8
View File
@@ -1,10 +1,10 @@
<template> <template>
<div class="scenery-info"> <div class="scenery-info">
<section> <section>
<div class="scenery-info-general" v-if="station.generalInfo"> <div class="scenery-info-general">
<SceneryInfoIcons :station="station" /> <SceneryInfoIcons :station="station" />
<div class="scenery-general-list"> <div class="scenery-general-list" v-if="station?.generalInfo">
<span> <span>
<b>{{ $t('availability.title') }}:</b> <b>{{ $t('availability.title') }}:</b>
{{ $t(`availability.${station.generalInfo.availability}`) }} {{ $t(`availability.${station.generalInfo.availability}`) }}
@@ -46,11 +46,11 @@
</span> </span>
</div> </div>
<SceneryInfoRoutes :station="station" /> <SceneryInfoRoutes v-if="station" :station="station" />
<div <div
class="scenery-authors" class="scenery-authors"
v-if="station.generalInfo.authors && station.generalInfo.authors.length > 0" v-if="station?.generalInfo?.authors && station.generalInfo.authors.length > 0"
> >
<b> <b>
{{ {{
@@ -102,13 +102,11 @@ export default defineComponent({
}, },
props: { props: {
station: { station: {
type: Object as PropType<Station>, type: Object as PropType<Station>
required: true
}, },
onlineScenery: { onlineScenery: {
type: Object as PropType<OnlineScenery>, type: Object as PropType<OnlineScenery>
required: false
} }
} }
}); });
@@ -12,7 +12,14 @@
class="dispatcher_name" class="dispatcher_name"
:to="`/journal/dispatchers?dispatcherName=${onlineScenery.dispatcherName}`" :to="`/journal/dispatchers?dispatcherName=${onlineScenery.dispatcherName}`"
> >
{{ onlineScenery.dispatcherName }} <span
class="text--donator"
v-if="isDonator(onlineScenery.dispatcherName)"
:title="$t('donations.dispatcher-message')"
>
{{ onlineScenery.dispatcherName }}
</span>
<span v-else>{{ onlineScenery.dispatcherName }}</span>
</router-link> </router-link>
<span class="dispatcher_likes text--primary"> <span class="dispatcher_likes text--primary">
@@ -36,9 +43,10 @@ import routerMixin from '../../../mixins/routerMixin';
import styleMixin from '../../../mixins/styleMixin'; import styleMixin from '../../../mixins/styleMixin';
import StationStatusBadge from '../../Global/StationStatusBadge.vue'; import StationStatusBadge from '../../Global/StationStatusBadge.vue';
import { OnlineScenery } from '../../../store/typings'; import { OnlineScenery } from '../../../store/typings';
import donatorMixin from '../../../mixins/donatorMixin';
export default defineComponent({ export default defineComponent({
mixins: [styleMixin, dateMixin, routerMixin], mixins: [styleMixin, dateMixin, routerMixin, donatorMixin],
props: { props: {
onlineScenery: { onlineScenery: {
type: Object as PropType<OnlineScenery>, type: Object as PropType<OnlineScenery>,
@@ -1,24 +1,33 @@
<template> <template>
<section class="info-icons"> <section class="info-icons">
<span <span v-if="!station || !station.generalInfo">
v-if="station.generalInfo && station.generalInfo.reqLevel >= 0" <img
class="scenery-icon icon-info level" class="icon-info"
:style="calculateExpStyle(station.generalInfo.reqLevel)" src="/images/icon-unknown.svg"
> alt="icon-unknown"
{{ station.generalInfo.reqLevel >= 2 ? station.generalInfo.reqLevel : 'L' }} :title="$t('desc.unknown')"
/>
</span> </span>
<span <span
v-if="station.generalInfo" v-if="station?.generalInfo && station?.generalInfo.reqLevel >= 0"
class="scenery-icon icon-info level"
:style="calculateExpStyle(station?.generalInfo.reqLevel)"
>
{{ station?.generalInfo.reqLevel >= 2 ? station?.generalInfo.reqLevel : 'L' }}
</span>
<span
v-if="station?.generalInfo"
class="scenery-icon icon-info" class="scenery-icon icon-info"
:class="station.generalInfo.controlType.replace('+', '-')" :class="station?.generalInfo.controlType.replace('+', '-')"
:title="$t('desc.control-type') + $t(`controls.${station.generalInfo.controlType}`)" :title="$t('desc.control-type') + $t(`controls.${station?.generalInfo.controlType}`)"
v-html="getControlTypeAbbrev(station.generalInfo.controlType)" v-html="getControlTypeAbbrev(station?.generalInfo.controlType)"
> >
</span> </span>
<img <img
v-if="station.generalInfo?.SUP" v-if="station?.generalInfo?.SUP"
class="icon-info" class="icon-info"
src="/images/icon-SUP.svg" src="/images/icon-SUP.svg"
alt="SUP (RASP-UZK)" alt="SUP (RASP-UZK)"
@@ -26,7 +35,7 @@
/> />
<img <img
v-if="station.generalInfo?.signalType" v-if="station?.generalInfo?.signalType"
class="icon-info" class="icon-info"
:src="`/images/icon-${station.generalInfo.signalType}.svg`" :src="`/images/icon-${station.generalInfo.signalType}.svg`"
:alt="station.generalInfo.signalType" :alt="station.generalInfo.signalType"
@@ -34,7 +43,7 @@
/> />
<img <img
v-if="station.generalInfo?.availability == 'nonPublic'" v-if="station?.generalInfo?.availability == 'nonPublic'"
class="icon-info" class="icon-info"
src="/images/icon-lock.svg" src="/images/icon-lock.svg"
alt="Non-public scenery" alt="Non-public scenery"
@@ -42,7 +51,7 @@
/> />
<img <img
v-if="station.generalInfo?.availability == 'unavailable'" v-if="station?.generalInfo?.availability == 'unavailable'"
class="icon-info" class="icon-info"
src="/images/icon-unavailable.svg" src="/images/icon-unavailable.svg"
alt="Unavailable scenery" alt="Unavailable scenery"
@@ -50,7 +59,7 @@
/> />
<img <img
v-if="station.generalInfo?.availability == 'abandoned'" v-if="station?.generalInfo?.availability == 'abandoned'"
class="icon-info" class="icon-info"
src="/images/icon-abandoned.svg" src="/images/icon-abandoned.svg"
alt="Abandoned scenery" alt="Abandoned scenery"
@@ -58,20 +67,12 @@
/> />
<img <img
v-if="station.generalInfo?.lines" v-if="station?.generalInfo?.lines"
class="icon-info" class="icon-info"
src="/images/icon-real.svg" src="/images/icon-real.svg"
alt="real scenery" alt="real scenery"
:title="`${$t('desc.real')} ${station.generalInfo.lines}`" :title="`${$t('desc.real')} ${station.generalInfo.lines}`"
/> />
<img
v-if="!station.generalInfo"
class="icon-info"
src="/images/icon-unknown.svg"
alt="icon-unknown"
:title="$t('desc.unknown')"
/>
</section> </section>
</template> </template>
@@ -85,8 +86,7 @@ export default defineComponent({
mixins: [stationInfoMixin, styleMixin], mixins: [stationInfoMixin, styleMixin],
props: { props: {
station: { station: {
type: Object as PropType<Station>, type: Object as PropType<Station>
required: true
} }
} }
}); });
+17 -11
View File
@@ -13,7 +13,7 @@
</span> </span>
</span> </span>
<span class="header_links"> <span class="header_links" v-if="station">
<a <a
:href="`https://pragotron-td2.web.app/board?name=${station.name}`" :href="`https://pragotron-td2.web.app/board?name=${station.name}`"
target="_blank" target="_blank"
@@ -48,7 +48,7 @@
<transition-group name="list-anim"> <transition-group name="list-anim">
<div <div
style="padding-bottom: 5em" style="padding-bottom: 5em"
v-if="store.dataStatuses.trains == 0 && computedScheduledTrains.length == 0" v-if="apiStore.dataStatuses.trains == 0 && computedScheduledTrains.length == 0"
key="list-loading" key="list-loading"
> >
<Loading /> <Loading />
@@ -187,10 +187,11 @@ import Loading from '../Global/Loading.vue';
import dateMixin from '../../mixins/dateMixin'; import dateMixin from '../../mixins/dateMixin';
import routerMixin from '../../mixins/routerMixin'; import routerMixin from '../../mixins/routerMixin';
import Station from '../../scripts/interfaces/Station'; import Station from '../../scripts/interfaces/Station';
import { useStore } from '../../store/mainStore'; import { useMainStore } from '../../store/mainStore';
import modalTrainMixin from '../../mixins/modalTrainMixin'; import modalTrainMixin from '../../mixins/modalTrainMixin';
import ScheduledTrainStatus from './ScheduledTrainStatus.vue'; import ScheduledTrainStatus from './ScheduledTrainStatus.vue';
import { OnlineScenery } from '../../store/typings'; import { OnlineScenery } from '../../store/typings';
import { useApiStore } from '../../store/apiStore';
export default defineComponent({ export default defineComponent({
name: 'SceneryTimetable', name: 'SceneryTimetable',
@@ -201,12 +202,10 @@ export default defineComponent({
props: { props: {
station: { station: {
type: Object as PropType<Station>, type: Object as PropType<Station>
required: true
}, },
onlineScenery: { onlineScenery: {
type: Object as PropType<OnlineScenery>, type: Object as PropType<OnlineScenery>
required: false
} }
}, },
@@ -226,7 +225,8 @@ export default defineComponent({
const route = useRoute(); const route = useRoute();
const currentURL = computed(() => `${location.origin}${route.fullPath}`); const currentURL = computed(() => `${location.origin}${route.fullPath}`);
const store = useStore(); const apiStore = useApiStore();
const mainStore = useMainStore();
const chosenCheckpoint = ref( const chosenCheckpoint = ref(
props.station?.generalInfo?.checkpoints?.length == 0 props.station?.generalInfo?.checkpoints?.length == 0
@@ -237,25 +237,29 @@ export default defineComponent({
return { return {
currentURL, currentURL,
chosenCheckpoint, chosenCheckpoint,
store apiStore,
mainStore
}; };
}, },
computed: { computed: {
tabliceZbiorczeHref() { tabliceZbiorczeHref() {
let url = `https://tablice-td2.web.app/?station=${this.station.name}`; let url = `https://tablice-td2.web.app/?station=${this.station!.name}`;
if (this.chosenCheckpoint) url += `&checkpoint=${this.chosenCheckpoint}`; if (this.chosenCheckpoint) url += `&checkpoint=${this.chosenCheckpoint}`;
return url; return url;
}, },
computedScheduledTrains() { computedScheduledTrains() {
if (!this.station) return [];
return ( return (
this.onlineScenery?.scheduledTrains this.onlineScenery?.scheduledTrains
?.filter( ?.filter(
(train) => (train) =>
train.checkpointName.toLocaleLowerCase() == train.checkpointName.toLocaleLowerCase() ==
(this.chosenCheckpoint || this.station.name).toLocaleLowerCase() (this.chosenCheckpoint || this.station!.name).toLocaleLowerCase() &&
train.region == this.mainStore.region.id
) )
.sort((a, b) => { .sort((a, b) => {
if (a.stopStatusID > b.stopStatusID) return 1; if (a.stopStatusID > b.stopStatusID) return 1;
@@ -272,6 +276,8 @@ export default defineComponent({
methods: { methods: {
loadSelectedOption() { loadSelectedOption() {
if (!this.station) return;
this.chosenCheckpoint = this.chosenCheckpoint =
this.station.generalInfo?.checkpoints[0]?.checkpointName || this.station.name; this.station.generalInfo?.checkpoints[0]?.checkpointName || this.station.name;
}, },
@@ -63,29 +63,26 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import axios from 'axios';
import { defineComponent, PropType } from 'vue'; import { defineComponent, PropType } from 'vue';
import dateMixin from '../../mixins/dateMixin'; import dateMixin from '../../mixins/dateMixin';
import Station from '../../scripts/interfaces/Station'; import Station from '../../scripts/interfaces/Station';
import { URLs } from '../../scripts/utils/apiURLs';
import Loading from '../Global/Loading.vue'; import Loading from '../Global/Loading.vue';
import listObserverMixin from '../../mixins/listObserverMixin'; import listObserverMixin from '../../mixins/listObserverMixin';
import { OnlineScenery } from '../../store/typings'; import { OnlineScenery } from '../../store/typings';
import { API } from '../../typings/api'; import { API } from '../../typings/api';
import { Status } from '../../typings/common'; import { Status } from '../../typings/common';
import http from '../../http';
export default defineComponent({ export default defineComponent({
name: 'SceneryTimetablesHistory', name: 'SceneryTimetablesHistory',
mixins: [dateMixin, listObserverMixin], mixins: [dateMixin, listObserverMixin],
props: { props: {
station: { station: {
type: Object as PropType<Station>, type: Object as PropType<Station>
required: true
}, },
onlineScenery: { onlineScenery: {
type: Object as PropType<OnlineScenery>, type: Object as PropType<OnlineScenery>
required: false
} }
}, },
@@ -103,10 +100,17 @@ export default defineComponent({
methods: { methods: {
async fetchAPIData(countFrom = 0, countLimit = 15) { async fetchAPIData(countFrom = 0, countLimit = 15) {
try { if (!this.station && !this.onlineScenery) {
const requestString = `${URLs.stacjownikAPI}/api/getTimetables?issuedFrom=${this.station.name}&countFrom=${countFrom}&countLimit=${countLimit}`; this.dataStatus = Status.Data.Loaded;
return;
}
const response: API.TimetableHistory.Response = await (await axios.get(requestString)).data; try {
const requestString = `api/getTimetables?issuedFrom=${
this.station?.name || this.onlineScenery?.name
}&countFrom=${countFrom}&countLimit=${countLimit}`;
const response: API.TimetableHistory.Response = await (await http.get(requestString)).data;
this.historyList = response; this.historyList = response;
@@ -117,7 +121,9 @@ export default defineComponent({
}, },
navigateToHistory() { navigateToHistory() {
this.$router.push(`/journal/timetables?issuedFrom=${this.station.name}`); this.$router.push(
`/journal/timetables?issuedFrom=${this.station?.name || this.onlineScenery?.name}`
);
} }
}, },
components: { Loading } components: { Loading }
@@ -139,7 +139,7 @@ import { defineComponent, inject } from 'vue';
import keyMixin from '../../mixins/keyMixin'; import keyMixin from '../../mixins/keyMixin';
import routerMixin from '../../mixins/routerMixin'; import routerMixin from '../../mixins/routerMixin';
import { useStationFiltersStore } from '../../store/stationFiltersStore'; import { useStationFiltersStore } from '../../store/stationFiltersStore';
import { useStore } from '../../store/mainStore'; import { useMainStore } from '../../store/mainStore';
import FilterOption from './FilterOption.vue'; import FilterOption from './FilterOption.vue';
import StorageManager from '../../managers/storageManager'; import StorageManager from '../../managers/storageManager';
@@ -163,7 +163,7 @@ export default defineComponent({
setup() { setup() {
const isVisible = inject('isFilterCardVisible'); const isVisible = inject('isFilterCardVisible');
const store = useStore(); const store = useMainStore();
const filterStore = useStationFiltersStore(); const filterStore = useStationFiltersStore();
return { return {
+262 -251
View File
@@ -1,277 +1,283 @@
<template> <template>
<section class="station_table"> <section class="station_table">
<table> <div class="table_wrapper">
<thead> <table>
<tr> <thead>
<th <tr>
v-for="headerName in headIds" <th
:key="headerName" v-for="headerName in headIds"
@click="changeSorter(headerName)" :key="headerName"
class="header-text" @click="changeSorter(headerName)"
class="header-text"
>
<span class="header_wrapper">
<div v-html="$t(`sceneries.${headerName}`)"></div>
<img
class="sort-icon"
v-if="sorterActive.headerName == headerName"
:src="`/images/icon-arrow-${sorterActive.dir == 1 ? 'asc' : 'desc'}.svg`"
alt="sort icon"
/>
</span>
</th>
<th
v-for="headerName in headIconsIds"
:key="headerName"
@click="changeSorter(headerName)"
class="header-image"
>
<span class="header_wrapper">
<img
:src="`/images/icon-${headerName}.svg`"
:alt="headerName"
:title="$t(`sceneries.${headerName}`)"
/>
<img
class="sort-icon"
v-if="sorterActive.headerName == headerName"
:src="`/images/icon-arrow-${sorterActive.dir == 1 ? 'asc' : 'desc'}.svg`"
alt="sort icon"
/>
</span>
</th>
</tr>
</thead>
<tbody>
<tr
class="station"
:class="{ 'last-selected': lastSelectedStationName == station.name }"
v-for="(station, i) in stations"
:key="i + station.name"
@click.left="setScenery(station.name)"
@click.right="openForumSite($event, station.generalInfo?.url)"
@keydown.enter="setScenery(station.name)"
@keydown.space="openForumSite($event, station.generalInfo?.url)"
tabindex="0"
> >
<span class="header_wrapper"> <td class="station_name" :class="station.generalInfo?.availability">
<div v-html="$t(`sceneries.${headerName}`)"></div> <b v-if="station.generalInfo?.project" style="color: salmon">{{
station.generalInfo.project
}}</b>
{{ station.name }}
</td>
<img <td class="station_level">
class="sort-icon" <span v-if="station.generalInfo">
v-if="sorterActive.headerName == headerName" <span
:src="`/images/icon-arrow-${sorterActive.dir == 1 ? 'asc' : 'desc'}.svg`" v-if="
alt="sort icon" station.generalInfo.reqLevel > -1 &&
station.generalInfo.availability != 'nonPublic' &&
station.generalInfo.availability != 'unavailable'
"
:style="calculateExpStyle(station.generalInfo.reqLevel)"
>
{{ station.generalInfo.reqLevel >= 2 ? station.generalInfo.reqLevel : 'L' }}
</span>
<span v-else-if="station.generalInfo.availability == 'abandoned'">
<img
src="/images/icon-abandoned.svg"
alt="non-public"
:title="$t('desc.abandoned')"
/>
</span>
<span v-else-if="station.generalInfo.availability == 'nonPublic'">
<img
src="/images/icon-lock.svg"
alt="non-public"
:title="$t('desc.non-public')"
/>
</span>
<span v-else>
<img
src="/images/icon-unavailable.svg"
alt="unavailable"
:title="$t('desc.unavailable')"
/>
</span>
</span>
<span v-else> ? </span>
</td>
<td class="station_status">
<StationStatusBadge
:isOnline="station.onlineInfo ? true : false"
:dispatcherStatus="station.onlineInfo?.dispatcherStatus"
/> />
</span> </td>
</th>
<th <td class="station_dispatcher-name">
v-for="headerName in headIconsIds" <span v-if="station.onlineInfo?.dispatcherName">
:key="headerName" <b
@click="changeSorter(headerName)" v-if="apiStore.donatorsData.includes(station.onlineInfo.dispatcherName)"
class="header-image" :title="$t('donations.dispatcher-message')"
> @click.stop="openDonationModal"
<span class="header_wrapper"> >
<img <img src="/images/icon-diamond.svg" alt="" />
:src="`/images/icon-${headerName}.svg`" {{ station.onlineInfo.dispatcherName }}
:alt="headerName" </b>
:title="$t(`sceneries.${headerName}`)"
/>
<img <div v-else>
class="sort-icon" {{ station.onlineInfo.dispatcherName }}
v-if="sorterActive.headerName == headerName" </div>
:src="`/images/icon-arrow-${sorterActive.dir == 1 ? 'asc' : 'desc'}.svg`" </span>
alt="sort icon" </td>
/>
</span>
</th>
</tr>
</thead>
<tbody> <td class="station_dispatcher-exp">
<tr <span
class="station" v-if="station.onlineInfo"
:class="{ 'last-selected': lastSelectedStationName == station.name }" :style="
v-for="(station, i) in stations" calculateExpStyle(
:key="i + station.name" station.onlineInfo.dispatcherExp,
@click.left="setScenery(station.name)" station.onlineInfo.dispatcherIsSupporter
@click.right="openForumSite($event, station.generalInfo?.url)" )
@keydown.enter="setScenery(station.name)" "
@keydown.space="openForumSite($event, station.generalInfo?.url)" >
tabindex="0" {{ station.onlineInfo.dispatcherExp < 2 ? 'L' : station.onlineInfo.dispatcherExp }}
> </span>
<td class="station_name" :class="station.generalInfo?.availability"> </td>
<b v-if="station.generalInfo?.project" style="color: salmon">{{
station.generalInfo.project
}}</b>
{{ station.name }}
</td>
<td class="station_level"> <td class="station_tracks twoway">
<span v-if="station.generalInfo">
<span <span
v-if=" v-if="
station.generalInfo.reqLevel > -1 && station.generalInfo &&
station.generalInfo.availability != 'nonPublic' && station.generalInfo.routes.twoWayCatenaryRouteNames.length > 0
station.generalInfo.availability != 'unavailable'
" "
:style="calculateExpStyle(station.generalInfo.reqLevel)" class="track catenary"
:title="`Liczba zelektryfikowanych szlaków dwutorowych: ${station.generalInfo.routes.twoWayCatenaryRouteNames.length}`"
> >
{{ station.generalInfo.reqLevel >= 2 ? station.generalInfo.reqLevel : 'L' }} {{ station.generalInfo.routes.twoWayCatenaryRouteNames.length }}
</span> </span>
<span v-else-if="station.generalInfo.availability == 'abandoned'"> <span
v-if="
station.generalInfo &&
station.generalInfo.routes.twoWayNoCatenaryRouteNames.length > 0
"
class="track no-catenary"
:title="`Liczba niezelektryfikowanych szlaków dwutorowych: ${station.generalInfo.routes.twoWayNoCatenaryRouteNames.length}`"
>
{{ station.generalInfo.routes.twoWayNoCatenaryRouteNames.length }}
</span>
<span class="separator"></span>
<span
v-if="
station.generalInfo &&
station.generalInfo.routes.oneWayCatenaryRouteNames.length > 0
"
class="track catenary"
:title="`Liczba zelektryfikowanych szlaków jednotorowych: ${station.generalInfo.routes.oneWayCatenaryRouteNames.length}`"
>
{{ station.generalInfo.routes.oneWayCatenaryRouteNames.length }}
</span>
<span
v-if="
station.generalInfo &&
station.generalInfo.routes.oneWayNoCatenaryRouteNames.length > 0
"
class="track no-catenary"
:title="`Liczba niezelektryfikowanych szlaków jednotorowych: ${station.generalInfo.routes.oneWayNoCatenaryRouteNames.length}`"
>
{{ station.generalInfo.routes.oneWayNoCatenaryRouteNames.length }}
</span>
</td>
<td class="station_info" v-if="station.generalInfo">
<span
class="scenery-icon icon-info"
:class="station.generalInfo.controlType.replace('+', '-')"
:title="$t('desc.control-type') + $t(`controls.${station.generalInfo.controlType}`)"
v-html="getControlTypeAbbrev(station.generalInfo.controlType)"
>
</span>
<span>
<img <img
src="/images/icon-abandoned.svg" class="icon-info"
alt="non-public" v-if="station.generalInfo.SUP"
:title="$t('desc.abandoned')" src="/images/icon-SUP.svg"
alt="SUP (RASP-UZK)"
:title="$t('desc.SUP')"
/> />
</span> </span>
<span v-else-if="station.generalInfo.availability == 'nonPublic'"> <span>
<img src="/images/icon-lock.svg" alt="non-public" :title="$t('desc.non-public')" />
</span>
<span v-else>
<img <img
src="/images/icon-unavailable.svg" class="icon-info"
alt="unavailable" v-if="station.generalInfo.signalType"
:title="$t('desc.unavailable')" :src="`/images/icon-${station.generalInfo.signalType}.svg`"
:alt="station.generalInfo.signalType"
:title="$t('desc.signals-type') + $t(`signals.${station.generalInfo.signalType}`)"
/> />
</span> </span>
</span>
<span v-else> ? </span> <span>
</td> <img
class="icon-info"
v-if="station.generalInfo && station.generalInfo.routes.sblRouteNames.length > 0"
src="/images/icon-SBL.svg"
alt="SBL"
:title="$t('desc.SBL') + `${station.generalInfo.routes.sblRouteNames.join(',')}`"
/>
</span>
</td>
<td class="station_status"> <td class="station_info" v-else>
<StationStatusBadge
:isOnline="station.onlineInfo ? true : false"
:dispatcherStatus="station.onlineInfo?.dispatcherStatus"
/>
</td>
<td class="station_dispatcher-name">
<span v-if="station.onlineInfo?.dispatcherName">
<b
v-if="store.donatorsData.includes(station.onlineInfo.dispatcherName)"
title="Dyżurny wspierający projekt Stacjownika!"
@click.stop="openDonationModal"
>
<img src="/images/icon-diamond.svg" alt="" />
{{ station.onlineInfo.dispatcherName }}
</b>
<div v-else>
{{ station.onlineInfo.dispatcherName }}
</div>
</span>
</td>
<td class="station_dispatcher-exp">
<span
v-if="station.onlineInfo"
:style="
calculateExpStyle(
station.onlineInfo.dispatcherExp,
station.onlineInfo.dispatcherIsSupporter
)
"
>
{{ station.onlineInfo.dispatcherExp < 2 ? 'L' : station.onlineInfo.dispatcherExp }}
</span>
</td>
<td class="station_tracks twoway">
<span
v-if="
station.generalInfo &&
station.generalInfo.routes.twoWayCatenaryRouteNames.length > 0
"
class="track catenary"
:title="`Liczba zelektryfikowanych szlaków dwutorowych: ${station.generalInfo.routes.twoWayCatenaryRouteNames.length}`"
>
{{ station.generalInfo.routes.twoWayCatenaryRouteNames.length }}
</span>
<span
v-if="
station.generalInfo &&
station.generalInfo.routes.twoWayNoCatenaryRouteNames.length > 0
"
class="track no-catenary"
:title="`Liczba niezelektryfikowanych szlaków dwutorowych: ${station.generalInfo.routes.twoWayNoCatenaryRouteNames.length}`"
>
{{ station.generalInfo.routes.twoWayNoCatenaryRouteNames.length }}
</span>
<span class="separator"></span>
<span
v-if="
station.generalInfo &&
station.generalInfo.routes.oneWayCatenaryRouteNames.length > 0
"
class="track catenary"
:title="`Liczba zelektryfikowanych szlaków jednotorowych: ${station.generalInfo.routes.oneWayCatenaryRouteNames.length}`"
>
{{ station.generalInfo.routes.oneWayCatenaryRouteNames.length }}
</span>
<span
v-if="
station.generalInfo &&
station.generalInfo.routes.oneWayNoCatenaryRouteNames.length > 0
"
class="track no-catenary"
:title="`Liczba niezelektryfikowanych szlaków jednotorowych: ${station.generalInfo.routes.oneWayNoCatenaryRouteNames.length}`"
>
{{ station.generalInfo.routes.oneWayNoCatenaryRouteNames.length }}
</span>
</td>
<td class="station_info" v-if="station.generalInfo">
<span
class="scenery-icon icon-info"
:class="station.generalInfo.controlType.replace('+', '-')"
:title="$t('desc.control-type') + $t(`controls.${station.generalInfo.controlType}`)"
v-html="getControlTypeAbbrev(station.generalInfo.controlType)"
>
</span>
<span>
<img <img
class="icon-info" class="icon-info"
v-if="station.generalInfo.SUP" src="/images/icon-unknown.svg"
src="/images/icon-SUP.svg" alt="icon-unknown"
alt="SUP (RASP-UZK)" :title="$t('desc.unknown')"
:title="$t('desc.SUP')"
/> />
</span> </td>
<span> <td class="station_users" :class="{ inactive: !station.onlineInfo }">
<img <span>{{ station.onlineInfo?.currentUsers || 0 }}</span>
class="icon-info" /
v-if="station.generalInfo.signalType" <span>{{ station.onlineInfo?.maxUsers || 0 }}</span>
:src="`/images/icon-${station.generalInfo.signalType}.svg`" </td>
:alt="station.generalInfo.signalType"
:title="$t('desc.signals-type') + $t(`signals.${station.generalInfo.signalType}`)"
/>
</span>
<span> <td class="station_spawns" :class="{ inactive: !station.onlineInfo }">
<img <span>{{ station.onlineInfo?.spawns.length || 0 }}</span>
class="icon-info" </td>
v-if="station.generalInfo && station.generalInfo.routes.sblRouteNames.length > 0"
src="/images/icon-SBL.svg"
alt="SBL"
:title="$t('desc.SBL') + `${station.generalInfo.routes.sblRouteNames.join(',')}`"
/>
</span>
</td>
<td class="station_info" v-else> <td
<img class="station_schedules all"
class="icon-info" style="width: 30px"
src="/images/icon-unknown.svg" :class="{ inactive: !station.onlineInfo }"
alt="icon-unknown" >
:title="$t('desc.unknown')" {{ station.onlineInfo?.scheduledTrainCount.all }}
/> </td>
</td>
<td class="station_users" :class="{ inactive: !station.onlineInfo }"> <td
<span>{{ station.onlineInfo?.currentUsers || 0 }}</span> class="station_schedules unconfirmed"
/ style="width: 30px"
<span>{{ station.onlineInfo?.maxUsers || 0 }}</span> :class="{ inactive: !station.onlineInfo }"
</td> >
{{ station.onlineInfo?.scheduledTrainCount.unconfirmed }}
</td>
<td class="station_spawns" :class="{ inactive: !station.onlineInfo }"> <td
<span>{{ station.onlineInfo?.spawns.length || 0 }}</span> class="station_schedules confirmed"
</td> style="width: 30px"
:class="{ inactive: !station.onlineInfo }"
<td >
class="station_schedules all" {{ station.onlineInfo?.scheduledTrainCount.confirmed }}
style="width: 30px" </td>
:class="{ inactive: !station.onlineInfo }" </tr>
> </tbody>
{{ station.onlineInfo?.scheduledTrainCount.all }} </table>
</td> </div>
<td
class="station_schedules unconfirmed"
style="width: 30px"
:class="{ inactive: !station.onlineInfo }"
>
{{ station.onlineInfo?.scheduledTrainCount.unconfirmed }}
</td>
<td
class="station_schedules confirmed"
style="width: 30px"
:class="{ inactive: !station.onlineInfo }"
>
{{ station.onlineInfo?.scheduledTrainCount.confirmed }}
</td>
</tr>
</tbody>
</table>
<Loading v-if="!isDataLoaded && stations.length == 0" /> <Loading v-if="!isDataLoaded && stations.length == 0" />
@@ -288,11 +294,12 @@ import stationInfoMixin from '../../mixins/stationInfoMixin';
import styleMixin from '../../mixins/styleMixin'; import styleMixin from '../../mixins/styleMixin';
import Station from '../../scripts/interfaces/Station'; import Station from '../../scripts/interfaces/Station';
import { useStationFiltersStore } from '../../store/stationFiltersStore'; import { useStationFiltersStore } from '../../store/stationFiltersStore';
import { useStore } from '../../store/mainStore'; import { useMainStore } from '../../store/mainStore';
import Loading from '../Global/Loading.vue'; import Loading from '../Global/Loading.vue';
import { HeadIdsTypes, headIconsIds, headIds } from '../../scripts/data/stationHeaderNames'; import { HeadIdsTypes, headIconsIds, headIds } from '../../scripts/data/stationHeaderNames';
import StationStatusBadge from '../Global/StationStatusBadge.vue'; import StationStatusBadge from '../Global/StationStatusBadge.vue';
import { Status } from '../../typings/common'; import { Status } from '../../typings/common';
import { useApiStore } from '../../store/apiStore';
export default defineComponent({ export default defineComponent({
props: { props: {
@@ -319,17 +326,19 @@ export default defineComponent({
}, },
setup() { setup() {
const store = useStore(); const mainStore = useMainStore();
const apiStore = useApiStore();
const stationFiltersStore = useStationFiltersStore(); const stationFiltersStore = useStationFiltersStore();
const isDataLoaded = computed(() => { const isDataLoaded = computed(() => {
return store.dataStatuses.sceneries != Status.Data.Loading; return apiStore.dataStatuses.sceneries != Status.Data.Loading;
}); });
return { return {
isDataLoaded, isDataLoaded,
stationFiltersStore, stationFiltersStore,
store mainStore,
apiStore
}; };
}, },
@@ -351,7 +360,7 @@ export default defineComponent({
openDonationModal(e: Event) { openDonationModal(e: Event) {
this.$emit('toggleDonationModal', true); this.$emit('toggleDonationModal', true);
this.store.modalLastClickedTarget = e.target; this.mainStore.modalLastClickedTarget = e.target;
}, },
openForumSite(e: Event, url: string | undefined) { openForumSite(e: Event, url: string | undefined) {
@@ -388,16 +397,18 @@ $rowCol: #424242;
} }
} }
.table_wrapper {
overflow: auto;
overflow-y: hidden;
font-weight: 500;
}
table { table {
white-space: nowrap; white-space: nowrap;
border-collapse: collapse; border-collapse: collapse;
// min-width: 1350px; // min-width: 1350px;
width: 100%; width: 100%;
overflow: auto;
overflow-y: hidden;
font-weight: 500;
@include smallScreen() { @include smallScreen() {
min-width: auto; min-width: auto;
} }
+12 -4
View File
@@ -35,8 +35,8 @@
<div class="train-driver"> <div class="train-driver">
<b <b
v-if="store.donatorsData.includes(train.driverName)" v-if="apiStore.donatorsData.includes(train.driverName)"
title="Dyżurny wspierający projekt Stacjownika!" :title="$t('donations.driver-message')"
> >
{{ train.driverName }} {{ train.driverName }}
<img src="/images/icon-diamond.svg" alt="donator diamond icon" /> <img src="/images/icon-diamond.svg" alt="donator diamond icon" />
@@ -80,10 +80,12 @@
<div class="status-badges"> <div class="status-badges">
<div v-if="!train.currentStationHash" class="train-badge offline"> <div v-if="!train.currentStationHash" class="train-badge offline">
<img src="/images/icon-offline.svg" alt="" />
{{ $t('trains.scenery-offline') }} {{ $t('trains.scenery-offline') }}
</div> </div>
<div v-if="!train.online" class="train-badge offline"> <div v-if="!train.online" class="train-badge offline">
<img src="/images/icon-offline.svg" alt="" />
Offline {{ lastSeenMessage(train.lastSeen) }} Offline {{ lastSeenMessage(train.lastSeen) }}
</div> </div>
</div> </div>
@@ -124,7 +126,8 @@ import trainInfoMixin from '../../mixins/trainInfoMixin';
import Train from '../../scripts/interfaces/Train'; import Train from '../../scripts/interfaces/Train';
import ProgressBar from '../Global/ProgressBar.vue'; import ProgressBar from '../Global/ProgressBar.vue';
import TrainThumbnail from '../Global/TrainThumbnail.vue'; import TrainThumbnail from '../Global/TrainThumbnail.vue';
import { useStore } from '../../store/mainStore'; import { useMainStore } from '../../store/mainStore';
import { useApiStore } from '../../store/apiStore';
export default defineComponent({ export default defineComponent({
mixins: [trainInfoMixin, styleMixin], mixins: [trainInfoMixin, styleMixin],
@@ -143,7 +146,8 @@ export default defineComponent({
data() { data() {
return { return {
store: useStore() store: useMainStore(),
apiStore: useApiStore()
}; };
} }
}); });
@@ -237,6 +241,10 @@ export default defineComponent({
flex-wrap: wrap; flex-wrap: wrap;
gap: 0.25em; gap: 0.25em;
img {
height: 15px;
}
} }
.general-timetable { .general-timetable {
+8 -5
View File
@@ -1,6 +1,6 @@
<template> <template>
<div class="filters-options" @keydown.esc="showOptions = false"> <div class="dropdown" @keydown.esc="showOptions = false">
<div class="bg" v-if="showOptions" @click="showOptions = false"></div> <div class="dropdown_background" v-if="showOptions" @click="showOptions = false"></div>
<button class="filter-button btn--filled btn--image" @click="toggleShowOptions" ref="button"> <button class="filter-button btn--filled btn--image" @click="toggleShowOptions" ref="button">
<img src="/images/icon-filter2.svg" alt="Open filters icon" /> <img src="/images/icon-filter2.svg" alt="Open filters icon" />
@@ -8,8 +8,8 @@
<span class="active-indicator" v-if="currentOptionsActive"></span> <span class="active-indicator" v-if="currentOptionsActive"></span>
</button> </button>
<transition name="options-anim"> <transition name="dropdown-anim">
<div class="options_wrapper" v-if="showOptions"> <div class="dropdown_wrapper" v-if="showOptions">
<div class="options_content"> <div class="options_content">
<h1 class="option-title">{{ $t('options.search-title') }}</h1> <h1 class="option-title">{{ $t('options.search-title') }}</h1>
<div class="search_content"> <div class="search_content">
@@ -87,6 +87,8 @@
</button> </button>
</div> </div>
</div> </div>
<div tabindex="0" @focus="showOptions = false"></div>
</div> </div>
</transition> </transition>
</div> </div>
@@ -184,7 +186,8 @@ export default defineComponent({
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import '../../styles/filters_options.scss'; @import '../../styles/dropdown.scss';
@import '../../styles/dropdown_filters.scss';
.search_content > div { .search_content > div {
margin: 0.5em auto; margin: 0.5em auto;
+2 -2
View File
@@ -72,7 +72,7 @@
import { computed, defineComponent, PropType } from 'vue'; import { computed, defineComponent, PropType } from 'vue';
import dateMixin from '../../mixins/dateMixin'; import dateMixin from '../../mixins/dateMixin';
import Train from '../../scripts/interfaces/Train'; import Train from '../../scripts/interfaces/Train';
import { useStore } from '../../store/mainStore'; import { useMainStore } from '../../store/mainStore';
import StopDate from '../Global/StopDate.vue'; import StopDate from '../Global/StopDate.vue';
import StockList from '../Global/StockList.vue'; import StockList from '../Global/StockList.vue';
import { TrainStop } from '../../store/typings'; import { TrainStop } from '../../store/typings';
@@ -92,7 +92,7 @@ export default defineComponent({
setup(props) { setup(props) {
return { return {
store: useStore(), store: useMainStore(),
lastConfirmed: computed(() => { lastConfirmed: computed(() => {
return props.train.timetableData!.followingStops.findIndex( return props.train.timetableData!.followingStops.findIndex(
+282
View File
@@ -0,0 +1,282 @@
<template>
<div class="dropdown" @keydown.esc="showOptions = false" @focusout="showOptions = false">
<div class="bg" v-if="showOptions" @click="showOptions = false"></div>
<button class="filter-button btn--filled btn--image" @click="toggleShowOptions" ref="button">
<img src="/images/icon-stats.svg" alt="Open filters icon" />
{{ $t('train-stats.stats-button') }}
</button>
<transition name="dropdown-anim">
<div class="dropdown_wrapper" v-if="showOptions">
<h1 class="text--primary">
<img src="/images/icon-stats.svg" alt="Open filters icon" />
{{ $t('train-stats.title') }}
</h1>
<hr style="margin: 0.5em 0" />
<div v-if="apiStore.dataStatuses.trains == Status.Loaded && regionTrains.length > 0">
<div class="top-list general">
<transition-group tag="ul" name="stats-anim">
<li class="badge" key="timetable-count">
<span>{{ $t('train-stats.timetable-count') }}</span>
<span>
<b>{{ regionTrainsWithTT.length }}</b>
</span>
</li>
<li class="badge" key="avg-speed">
<span>{{ $t('train-stats.avg-speed') }}</span>
<span>
<b>{{ stats.avgSpeed.toFixed(1) }} km/h</b>
</span>
</li>
<li class="badge" key="avg-distance">
<span>{{ $t('train-stats.avg-timetable') }}</span>
<span>
<b>{{ stats.avgDistance.toFixed(1) }} km</b>
</span>
</li>
</transition-group>
</div>
<div class="top-list categories">
<h3>{{ $t('train-stats.top-categories') }}</h3>
<transition-group tag="ul" name="stats-anim">
<li class="badge" v-for="top in stats.topCategories" :key="top.name">
<span>{{ top.name }}</span>
<span>{{ top.count }}</span>
</li>
</transition-group>
<span class="no-data" v-if="stats.topCategories.length == 0">
{{ $t('train-stats.no-timetables') }}
</span>
</div>
<div class="top-list vehicles">
<h3>{{ $t('train-stats.top-vehicles') }}</h3>
<transition-group tag="ul" name="stats-anim">
<li class="badge" v-for="top in stats.topVehicles" :key="top.name">
<span>{{ top.name }}</span>
<span>{{ top.count }}</span>
</li>
</transition-group>
<span class="no-data" v-if="stats.topVehicles.length == 0">
{{ $t('train-stats.no-vehicles') }}
</span>
</div>
<div class="top-list vehicle-types">
<h3>{{ $t('train-stats.top-units') }}</h3>
<transition-group tag="ul" name="stats-anim">
<li class="badge" v-for="top in stats.topUnits.slice(0, 7)" :key="top.name">
<span>{{ top.name }}</span>
<span>{{ top.count }}</span>
</li>
</transition-group>
<span class="no-data" v-if="stats.topUnits.length == 0">
{{ $t('train-stats.no-units') }}
</span>
</div>
</div>
<div v-else-if="apiStore.dataStatuses.trains != Status.Loaded">
{{ $t('train-stats.stats-loading') }}
</div>
<div class="no-data" v-else>
{{ $t('train-stats.no-stats') }}
</div>
</div>
</transition>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { useMainStore } from '../../store/mainStore';
import { Status } from '../../typings/common';
import { useApiStore } from '../../store/apiStore';
interface ITop {
name: string;
count: number;
}
interface IStats {
timetableCount: number;
avgSpeed: number;
avgDistance: number;
topCategories: ITop[];
topVehicles: ITop[];
topUnits: ITop[];
}
function compareTop(top1: ITop, top2: ITop) {
return Math.sign(top2.count - top1.count) || top1.name.localeCompare(top2.name, 'pl-PL');
}
export default defineComponent({
data() {
return {
showOptions: false,
store: useMainStore(),
apiStore: useApiStore(),
Status: Status.Data
};
},
computed: {
regionTrains() {
return this.store.trainList.filter((train) => train.region == this.store.region.id);
},
regionTrainsWithTT() {
return this.regionTrains.filter((train) => train.timetableData);
},
stats() {
const stats = this.regionTrains.reduce(
(acc, train, i, arr) => {
// AVG SPEED
acc.avgSpeed += train.speed / arr.length;
// TOP VEHICLES
const locoType = train.locoType.split('-')[0];
const topVehicle = acc.topVehicles.find((top) => top.name == locoType);
if (!topVehicle) acc.topVehicles.push({ name: locoType, count: 1 });
else topVehicle.count++;
// TOP UNITS
const unitType = train.locoType;
const topUnit = acc.topUnits.find((top) => top.name == unitType);
if (!topUnit) acc.topUnits.push({ name: unitType, count: 1 });
else topUnit.count++;
if (train.timetableData !== undefined) {
acc.timetableCount++;
// AVG DISTANCE
acc.avgDistance += train.timetableData.routeDistance;
// TOP CATEGORIES
const topCategory = acc.topCategories.find(
(top) => top.name == train.timetableData!.category
);
if (!topCategory)
acc.topCategories.push({ name: train.timetableData!.category, count: 1 });
else topCategory.count++;
}
if (i == arr.length - 1 && acc.timetableCount != 0) {
acc.avgDistance /= acc.timetableCount;
}
return acc;
},
{
timetableCount: 0,
avgDistance: 0,
avgSpeed: 0,
topCategories: [],
topUnits: [],
topVehicles: []
} as IStats
);
stats.topCategories.sort(compareTop);
stats.topUnits.sort(compareTop);
stats.topVehicles.sort(compareTop);
return stats;
}
},
methods: {
toggleShowOptions() {
this.showOptions = !this.showOptions;
this.$nextTick(() => {
if (this.showOptions) (this.$refs['button'] as HTMLButtonElement)?.focus();
});
}
}
});
</script>
<style lang="scss" scoped>
@import '../../styles/dropdown.scss';
@import '../../styles/badge.scss';
@import '../../styles/responsive.scss';
h1 img {
vertical-align: text-bottom;
}
h3 {
margin: 0.5em 0;
}
.no-data {
font-size: 1.1em;
color: #ccc;
}
.top-list ul {
display: flex;
flex-wrap: wrap;
gap: 0.5em;
// @include smallScreen {
// justify-content: center;
// }
}
.badge {
margin: 0;
& > span:first-child {
background-color: $accentCol;
color: black;
}
}
.dropdown_wrapper {
max-width: 600px;
}
.stats-anim {
&-move,
&-enter-active,
&-leave-active {
transition: all 250ms ease;
}
&-enter-from,
&-leave-to {
opacity: 0;
transform: translateX(5px);
}
&-leave-active {
position: absolute;
}
}
@include smallScreen {
h1,
.no-data {
text-align: center;
}
}
</style>
+9 -6
View File
@@ -1,16 +1,16 @@
<template> <template>
<transition name="status-anim" mode="out-in" tag="div" class="train-table"> <transition name="status-anim" mode="out-in" tag="div" class="train-table">
<div :key="store.dataStatuses.trains"> <div :key="apiStore.dataStatuses.trains">
<div class="table-info" key="offline" v-if="store.isOffline"> <div class="table-info" key="offline" v-if="store.isOffline">
{{ $t('app.offline') }} {{ $t('app.offline') }}
</div> </div>
<Loading v-else-if="trains.length == 0 && store.dataStatuses.trains == 0" key="loading" /> <Loading v-else-if="trains.length == 0 && apiStore.dataStatuses.trains == 0" key="loading" />
<div <div
class="table-info" class="table-info"
key="no-trains" key="no-trains"
v-else-if="trains.length == 0 && store.dataStatuses.trains != 0" v-else-if="trains.length == 0 && apiStore.dataStatuses.trains != 0"
> >
{{ $t('trains.no-trains') }} {{ $t('trains.no-trains') }}
</div> </div>
@@ -35,10 +35,11 @@
import { defineComponent, inject, PropType, Ref } from 'vue'; import { defineComponent, inject, PropType, Ref } from 'vue';
import modalTrainMixin from '../../mixins/modalTrainMixin'; import modalTrainMixin from '../../mixins/modalTrainMixin';
import Train from '../../scripts/interfaces/Train'; import Train from '../../scripts/interfaces/Train';
import { useStore } from '../../store/mainStore'; import { useMainStore } from '../../store/mainStore';
import Loading from '../Global/Loading.vue'; import Loading from '../Global/Loading.vue';
import TrainInfo from './TrainInfo.vue'; import TrainInfo from './TrainInfo.vue';
import { Status } from '../../typings/common'; import { Status } from '../../typings/common';
import { useApiStore } from '../../store/apiStore';
export default defineComponent({ export default defineComponent({
components: { Loading, TrainInfo }, components: { Loading, TrainInfo },
@@ -53,7 +54,8 @@ export default defineComponent({
mixins: [modalTrainMixin], mixins: [modalTrainMixin],
setup() { setup() {
const store = useStore(); const store = useMainStore();
const apiStore = useApiStore();
const searchedTrain = inject('searchedTrain') as Ref<string>; const searchedTrain = inject('searchedTrain') as Ref<string>;
const searchedDriver = inject('searchedDriver') as Ref<string>; const searchedDriver = inject('searchedDriver') as Ref<string>;
@@ -61,6 +63,7 @@ export default defineComponent({
searchedTrain, searchedTrain,
searchedDriver, searchedDriver,
store, store,
apiStore,
sorterActive: inject('sorterActive') as { sorterActive: inject('sorterActive') as {
id: string | number; id: string | number;
dir: number; dir: number;
@@ -72,7 +75,7 @@ export default defineComponent({
dataStatus() { dataStatus() {
if (this.store.isOffline) return Status.Data.Offline; if (this.store.isOffline) return Status.Data.Offline;
if (this.trains.length == 0 && this.store.dataStatuses.trains == Status.Data.Loading) if (this.trains.length == 0 && this.apiStore.dataStatuses.trains == Status.Data.Loading)
return Status.Data.Loading; return Status.Data.Loading;
return Status.Data.Loaded; return Status.Data.Loaded;
File diff suppressed because it is too large Load Diff
+10
View File
@@ -0,0 +1,10 @@
import axios from 'axios';
const http = axios.create({
baseURL:
import.meta.env.VITE_API_MODE === 'development'
? 'http://localhost:3001'
: 'https://stacjownik.spythere.eu'
});
export default http;
+23 -5
View File
@@ -9,12 +9,15 @@
"p2-b3": "Stacjobot (Stacjownik bot)", "p2-b3": "Stacjobot (Stacjownik bot)",
"p2-a1": "Discord server", "p2-a1": "Discord server",
"p3": "<b>If you have the means and would like to support my work, I would be grateful for any financial assistance that could help cover at least some of the server costs and further enhance the capabilities of the application!</b>", "p3": "<b>If you have the means and would like to support my work, I would be grateful for any financial assistance that could help cover at least some of the server costs and further enhance the capabilities of the application!</b>",
"p4": "Every person who decides to contribute at least {b1} for the development of Stacjownik, will receive (upon a personal request) {img}{b2} of username in the \"Sceneries\" and \"Trains\" tabs of the application, as well as on my Discord server (after verifying the payment author, preferably by providing the username directly with the payment).", "p4": "Every person who decides to contribute at least {b1} (in case of PayPal it must be a payment including additional transaction fees) for the development of Stacjownik, will receive (upon a personal request) {img}{b2} of username in the app and on my Discord server (after verifying the payment author, preferably by providing the username directly with the payment).",
"p4-b1": "5 PLN", "p4-b1": "5 PLN",
"p4-b2": "a symbolic highlight", "p4-b2": "a symbolic highlight",
"p5": "Thank you and enjoy the app!<br />~ Spythere", "p5": "Thank you and enjoy the app!<br />~ Spythegre",
"action-exit": "Maybe next time...", "action-exit": "Maybe next time...",
"action-confirm": "DONATE!" "action-paypal": "DONATE WITH PAYPAL",
"action-buycoffee": "BUY ME A COFFEE!",
"dispatcher-message": "Dispatcher supporting the Stacjownik project!",
"driver-message": "Driver supporting the Stacjownik project!"
}, },
"general": { "general": {
"and": " and ", "and": " and ",
@@ -292,6 +295,21 @@
"scenery-offline": "Offline ride", "scenery-offline": "Offline ride",
"timeout": "An error occured while trying to refresh SWDR timetable data!" "timeout": "An error occured while trying to refresh SWDR timetable data!"
}, },
"train-stats": {
"stats-button": "STATISTICS",
"title": "STATISTICS ONLINE",
"timetable-count": "ACTIVE TIMETABLES",
"avg-speed": "AVG SPEED",
"avg-timetable": "AVG TIMETABLE",
"top-categories": "Timetable categories",
"top-vehicles": "Vehicles online",
"top-units": "Common units online",
"stats-loading": "Loading...",
"no-timetables": "No active timetables in this region!",
"no-vehicles": "No active vehicles in this region!",
"no-units": "No active units in this region!",
"no-stats": "No statistics available for the current region!"
},
"journal": { "journal": {
"title": "DISPATCHER HISTORY", "title": "DISPATCHER HISTORY",
"loading": "Loading dispatcher history data...", "loading": "Loading dispatcher history data...",
@@ -387,8 +405,8 @@
"two-way-routes": "Two way routes", "two-way-routes": "Two way routes",
"option-active-timetables": "Active timetables", "option-active-timetables": "Active timetables",
"option-timetables-history": "Timetables history", "option-timetables-history": "Timetables history PL1",
"option-dispatchers-history": "Dispatchers history", "option-dispatchers-history": "Dispatchers history PL1",
"timetable-author-title": "Issued by", "timetable-author-title": "Issued by",
"timetable-author-unknown": "Author unknown", "timetable-author-unknown": "Author unknown",
-434
View File
@@ -1,434 +0,0 @@
{
"general": {
"and": " oraz ",
"refresh": "ODŚWIEŻ",
"TWR": "Towar niebezpieczny wysokiego ryzyka",
"SKR": "Przekroczona skrajnia"
},
"app": {
"sceneries": "SCENERIE",
"trains": "POCIĄGI",
"journal": "DZIENNIK",
"loading": "Pobieranie danych...",
"support": "Wspomóż projekt",
"error": "Wystąpił problem z załadowaniem danych!",
"no-result": "Brak wyników o podanych kryteriach!",
"migration-warning": "Usługi Stacjownika będą niedostępne w godzinach 1:00-3:00 2 czerwca 2022r. z powodu migracji hostingów API!",
"migration-confirm": "Przyjąłem!",
"offline": "Aplikacja w trybie offline!"
},
"footer": {
"discord": "Serwer Discord Stacjownika"
},
"update": {
"title": "Nowa wersja Stacjownika jest dostępna!",
"paragraph1": "Miłego korzystania z aplikacji i niech S2 będzie z wami!",
"release-link": "Kliknij, aby przejrzeć listę zmian (GitHub)",
"confirm-button": "ZAKTUALIZUJ",
"later-button": "PÓŹNIEJ"
},
"data-status": {
"S1-offline": "<b>Sygnał S1</b> <br> Aplikacja działa w trybie offline!",
"S1a-connection": "<b>Sygnał S1a</b> <br> Błąd podczas próby połączenia się z API Stacjownika!",
"S1a-sceneries": "<b>Sygnał S1a</b> <br> Błąd podczas pobierania danych o sceneriach online!",
"S2": "<b>Sygnał S2</b> <br> Pomyślnie załadowano dane!",
"S3": "<b>Sygnał S3</b> <br> Pobieranie danych...",
"S5-timetables": "<b>Sygnał S5</b> <br> Rozkłady jazdy mogą być niekompletne!",
"S5-dispatchers": "<b>Sygnał S5</b> <br> Błąd podczas pobierania danych o statusach dyżurnych ruchu!",
"S5-trains": "<b>Sygnał S5</b> <br> Błąd podczas pobierania danych o pociągach online!"
},
"desc": {
"control-type": "Sterowanie: ",
"signals-type": "Sygnalizacja: ",
"SBL": "Sceneria posiada SBL na szlakach: ",
"SUP": "Wymaga programu SUP do kontroli systemu RASP-UZK",
"TWB-all": "Sceneria posiada blokadę dwukierunkową na wszystkich szlakach",
"TWB-routes": "Sceneria posiada blokadę dwukierunkową na szlakach: ",
"default": "Sceneria dostępna domyślnie w paczce z grą",
"non-public": "Sceneria niepubliczna",
"unavailable": "Sceneria niedostępna",
"unknown": "Nieznana sceneria",
"real": "Sceneria z realnymi liniami kolejowymi: ",
"abandoned": "Sceneria wycofana z rozgrywki"
},
"signals": {
"title": "Sygnalizacja",
"współczesna": "współczesna",
"mieszana": "mieszana",
"kształtowa": "kształtowa",
"historyczna": "historyczna"
},
"controls": {
"title": "Sterowanie",
"SPK": "SPK",
"SCS": "SCS",
"SCS-SPK": "SCS/SPK",
"SPE": "SPE",
"ręczne": "ręczne",
"ręczne+SPK": "ręczne z SPK",
"ręczne+SCS": "ręczne z SCS",
"mechaniczne": "mechaniczne",
"mechaniczne+SPK": "mechaniczne z SPK",
"mechaniczne+SCS": "mechaniczne z SCS"
},
"status": {
"online": "DO ",
"free": "WOLNA",
"ending": "KOŃCZY",
"not-signed": "NIEZALOGOWANY",
"no-limit": "BEZ LIMITU",
"unavailable": "NIEDOSTĘPNY",
"brb": "Z/W",
"no-space": "BRAK MIEJSCA",
"unknown": "NIEZNANY"
},
"options": {
"filters": "FILTRY",
"donate": "WESPRZYJ",
"search-button": "Szukaj",
"reset-button": "Zresetuj",
"sort-title": "SORTUJ WG:",
"filter-title": "FILTRUJ WG:",
"search-title": "SZUKAJ:",
"search-train-no": "Nr pociągu",
"search-train": "Nr pociągu / #",
"search-driver": "Nick maszynisty",
"search-dispatcher": "Nick dyżurnego",
"search-station": "Nazwa scenerii",
"search-author": "Nick autora rozkładu jazdy",
"search-issuedFrom": "Sceneria początkowa",
"search-timetables-date": "Data rozkładu jazdy (UTC+2 / CEST)",
"search-dispatchers-date": "Data służby (UTC+2 / CEST)",
"search-date": "Data (UTC+2 / CEST)",
"sort-routeDistance": "kilometraż",
"sort-allStopsCount": "stacje",
"sort-beginDate": "data",
"sort-timetableId": "ID rozkładu",
"sort-timestampFrom": "data",
"sort-duration": "czas dyżuru",
"sort-id": "id rozkładu",
"sort-mass": "masa",
"sort-speed": "prędkość",
"sort-length": "długość",
"sort-timetable": "nr pociągu",
"sort-progress": "przebyta trasa",
"sort-delay": "opóźnienie",
"sort-comments": "uwagi ekspl.",
"filter-withComments": "UWAGI EKSPLOATACYJNE",
"filter-noComments": "BEZ UWAG",
"filter-twr": "WYS. RYZYKA",
"filter-skr": "SKRAJNIA",
"filter-twr-skr": "WSZYSTKIE",
"filter-common": "ZWYKŁE",
"filter-passenger": "PASAŻERSKIE",
"filter-freight": "TOWAROWE",
"filter-other": "INNE",
"filter-noTimetable": "BEZ RJ",
"filter-withTimetable": "ROZKŁAD JAZDY",
"filter-reset": "ZRESETUJ FILTRY",
"filter-clear": "WYŁĄCZ FILTRY",
"filter-section-timetable-status": "STATUS ROZKŁADU JAZDY",
"filter-section-twrskr": "UWAGI",
"filter-all": "WSZYSTKIE",
"filter-abandoned": "PORZUCONE",
"filter-fulfilled": "WYPEŁNIONE",
"filter-active": "AKTYWNE"
},
"filters": {
"desc": " &bull; Kliknięcie: zaznaczenie / odznaczenie filtru <br /> &bull; Podwójne kliknięcie: odznaczenie reszty filtrów z <b class='text--primary'>grupy</b> <br /> &bull; <span style='color: coral'>RESET</span>: zresetowanie filtrów z <b class='text--primary'>grupy</b>",
"sections": {
"quick": "SZYBKIE FILTRY",
"reality": "FIKCYJNOŚĆ SCENERII",
"package-access": "DOSTĘPNOŚĆ W PACZCE",
"access": "DOSTĘPNOŚĆ OGÓLNA",
"control": "TYP STEROWANIA",
"signals": "TYP SYGNALIZACJI",
"addons": "DODATKOWE PROGRAMY",
"blockades": "BLOKADY LINIOWE",
"status": "STATUS ONLINE"
},
"all-available": "WSZYSTKIE DOSTĘPNE",
"all-free": "WSZYSTKIE WOLNE",
"endingStatus": "KOŃCZY",
"afkStatus": "Z/W",
"noSpaceStatus": "BRAK MIEJSCA",
"unavailableStatus": "NIEDOSTĘPNY",
"title": "FILTRUJ STACJE",
"default": "DOMYŚLNA",
"not-default": "POZA PACZKĄ",
"real": "REALNA",
"fictional": "FIKCYJNA",
"unavailable": "NIEDOSTĘPNA",
"non-public": "NIEPUBLICZNA",
"abandoned": "WYCOFANA",
"SPK": "SPK",
"SPK-R": "SPK + RĘCZNE",
"SPK-M": "SPK + MECH.",
"SCS": "SCS",
"SCS-R": "SCS + RĘCZNE",
"SCS-M": "SCS + MECH.",
"SPE": "SPE",
"manual": "RĘCZNE",
"SUP": "SUP (RASP-UZK)",
"noSUP": "BEZ SUP",
"SBL": "SAMOCZYNNA",
"PBL": "PÓŁSAMOCZYNNA",
"mechanical": "MECHANICZNE",
"modern": "WSPÓŁCZESNA",
"semaphores": "KSZTAŁTOWA",
"mixed": "MIESZANA",
"historical": "HISTORYCZNA",
"free": "WOLNA",
"occupied": "ZAJĘTA",
"sliders": {
"min-lvl": "MIN. WYMAGANY POZIOM DYŻURNEGO",
"max-lvl": "MAKS. WYMAGANY POZIOM DYŻURNEGO",
"routes-1t-cat": "SZLAKI JEDNOTOROWE ZELEKTR. (MINIMUM)",
"routes-1t-other": "SZLAKI JEDNOTOROWE NIEZELEKTR. (MINIMUM)",
"routes-2t-cat": "SZLAKI DWUTOROWE ZELEKTR. (MINIMUM)",
"routes-2t-other": "SZLAKI DWUTOROWE NIEZELEKTR. (MINIMUM)"
},
"authors-search": "Szukaj autora (uwzględnia inne filtry)",
"minimum-hours-title": "POKAŻ TYLKO SCENERIE DOSTĘPNE MINIMUM DO:",
"now": "TERAZ",
"hour": " godz.",
"no-limit": "BEZ LIMITU",
"include-selected": "POKAŻ ZAZNACZONE",
"save": "ZAPAMIĘTAJ FILTRY",
"reset": "RESETUJ FILTRY",
"close": "ZAMKNIJ FILTRY"
},
"sceneries": {
"station": "Stacja",
"abbr": "Skrót\nposterunku",
"min-lvl": "Min. poziom\ndyżurnego",
"status": "Status",
"dispatcher": "Dyżurny",
"dispatcher-lvl": "Poziom\ndyżurnego",
"routes": "Szlaki\n2tor / 1tor",
"general": "Informacje\nogólne",
"user": "Maszyniści online",
"spawn": "Otwarte spawny",
"timetableAll": "Aktywne rozkłady jazdy",
"timetableConfirmed": "Zatwierdzone rozkłady jazdy",
"timetableUnconfirmed": "Niezatwierdzone rozkłady jazdy",
"no-stations": "Brak stacji do wyświetlenia!",
"scenery-search": "Wyszukaj scenerię..."
},
"trains": {
"no-trains": "Brak pociągów do wyświetlenia!",
"loading": "Pobieranie danych o pociągach...",
"offline": "Przejazd offline",
"stats": "STATYSTYKI RUCHU",
"stats-speed": "PRĘDKOŚCI POCIĄGÓW (MIN, ŚR, MAX) [km/h]",
"stats-length": "DŁUGOŚCI ROZKŁADÓW (MIN, ŚR, MAX) [km]",
"stats-categories": "KATEGORIE RJ",
"stats-special-twr": "WYSOKIEGO RYZYKA",
"stats-special-skr": "PRZEKROCZONA SKRAJNIA",
"stats-locos": "NAJCZĘSTSZE JEDNOSTKI",
"current-scenery": "na scenerii",
"current-signal": "przy semaforze",
"current-track": "na szlaku",
"delayed": "Opóźniony: ",
"preponed": "Przed czasem: ",
"on-time": "Planowo",
"route-progress": "Postęp: ",
"detailed-timetable": "Szczegółowy rozkład jazdy pociągu ",
"via-title": "Przez: ",
"no-timetable": "brak rozkładu jazdy",
"distance-exceeded": "Uwaga! Z powodu wewnętrznego błędu serwera TD2, rozkłady jazdy o kilometrażu powyżej 200km mogą być niepoprawne!",
"cars": "Wagony",
"EZT": "EZT",
"SZT": "SZT",
"loco-electric": "Elektrowóz",
"loco-diesel": "Spalinowóz",
"timetable-comments": "Pociąg z uwagami eksploatacyjnymi",
"comment": "Uwagi eksploatacyjne dla: ",
"table-limit": "Dla płynności działania strony pokazanych jest tylko 10 pociągów zgodnie z wybranymi filtrami.",
"last-seen-now": "od niedawna",
"last-seen-min": "od minuty",
"last-seen-ago": "od {minutes} minut",
"scenery-offline": "Przejazd offline",
"timeout": "Wystąpił problem z aktualizacją rozkładów jazdy z SWDR"
},
"journal": {
"title": "HISTORIA DYŻURÓW",
"loading": "Ładowanie historii dyżurów...",
"no-history": "Brak historii dyżurów dla tej scenerii!",
"data-refreshed-at": "Dane odświeżone o",
"section-timetables": "ROZKŁADY JAZDY",
"section-dispatchers": "DYŻURNI",
"no-further-data": "Brak dalszych wyników dla podanych parametrów",
"loading-further-data": "Ładowanie...",
"online-since": "ONLINE OD",
"duty-lasted": "Dyżur trwał",
"hours": "{value} godz.",
"minutes": "{value} min.",
"seconds": "{value} sek.",
"route-length": "Kilometraż:",
"station-count": "Stacje:",
"dispatcher-name": "Autor",
"timetable-day": "Rozkład z dnia",
"timetable-active": "AKTYWNY",
"timetable-fulfilled": "WYPEŁNIONY",
"timetable-abandoned": "PORZUCONY",
"stock-info": "DODATKOWE INFORMACJE",
"stock-length": "Długość",
"stock-mass": "Masa",
"stock-max-speed": "Prędkość maks.",
"load-data": "Pobierz dalszą historię...",
"stats-title": "STATYSTYKI MASZYNISTY",
"last-seen-at": "Ostatnio widziany na: ",
"currently-at": "Obecnie na scenerii: ",
"stats-timetables": "ROZKŁADY JAZDY",
"stats-longest-timetable": "NAJDŁUŻSZY RJ",
"stats-avg-timetable": "ŚREDNIA DŁUGOŚĆ RJ",
"stats-distance": "DYSTANS",
"stats-stations": "STACJE",
"timetable-stats-total": "Stworzone rozkłady jazdy: {count} (łączny dystans: {distance})",
"timetable-stats-longest": "Najdłuższy rozkład jazdy: #{id} (stworzony przez dyżurnego {author} dla maszynisty {driver} o dystansie {distance})",
"timetable-stats-most-active-dr": "Najaktywniejszy dyżurny: {dispatcher} (stworzył {count})",
"timetable-stats-most-active-dr-many": "Najaktywniejsi dyżurni: {dispatchers} (stworzyli po {count})",
"timetable-stats-most-active-driver": "Najaktywniejszy maszynista: {driver} (łączny przejechany dystans: {distance})",
"timetable-stats-longest-duties": "Najdłuższa służba: {dispatcher} na scenerii {station} (czas trwania: {duration})",
"timetable-count": "rozkład jazdy | rozkładów jazdy",
"daily-stats-title": "STATYSTYKI DNIA",
"daily-stats-info": "Dzisiejsze statystyki nie są jeszcze dostępne!",
"driver-stats-title": "STATYSTYKI GRACZA",
"driver-stats-info": "Wpisz nazwę użytkownika w filtrach [F], aby zobaczyć jego statystyki maszynisty!",
"stats-loading": "Pobieranie statystyk...",
"stats-error": "Ups! Wystąpił błąd podczas próby pobrania statystyk! :/",
"timetable-location-signal": "semafor:",
"timetable-location-route": "szlak:",
"history-name": "Sceneria",
"history-hash": "Hash",
"history-dispatcher": "Dyżurny",
"history-level": "Poziom",
"history-rate": "Ocena",
"history-region": "Region",
"history-date": "Data służby"
},
"scenery": {
"users": "GRACZE ONLINE",
"spawns": "OTWARTE SPAWNY",
"timetables": "AKTYWNE ROZKŁADY JAZDY",
"no-timetables": "Brak aktywnych rozkładów!",
"offline": "Sceneria jest offline",
"no-users": "BRAK AKTYWNYCH GRACZY",
"no-spawns": "BRAK OTWARTYCH SPAWNÓW",
"no-scenery": "Ups! Ta sceneria nie istnieje!",
"return-btn": "Wróć na stronę główną",
"history-btn": "Przejdź do widoku historii dyżurnych ruchu",
"info-btn": "Wróć do widoku scenerii",
"authors-title": "Autor scenerii | Autorzy scenerii",
"abbrev": "Skrót posterunku:",
"lines-title": "Rzeczywiste linie",
"project-title": "Projekt",
"one-way-routes": "Szlaki jednotorowe",
"two-way-routes": "Szlaki dwutorowe",
"option-active-timetables": "Aktywne rozkłady jazdy",
"option-timetables-history": "Historia rozkładów",
"option-dispatchers-history": "Historia dyżurów",
"timetable-author-title": "Wydany przez",
"timetable-author-unknown": "Autor nieznany",
"timetables-history-id": "ID",
"timetables-history-number": "Numer",
"timetables-history-route": "Trasa",
"timetables-history-driver": "Maszynista",
"timetables-history-author": "Autor RJ",
"timetables-history-date": "Data",
"dispatchers-history-hash": "Hash",
"dispatchers-history-dispatcher": "Dyżurny",
"dispatchers-history-level": "Poziom",
"dispatchers-history-rate": "Ocena",
"dispatchers-history-date": "Data służby",
"req-level": "ogólnodostępna | minimum {lvl} poziom dyżurnego | minimum {lvl} poziom dyżurnego",
"history-list-empty": "Brak historii dla tej scenerii!",
"forum-topic": "Oficjalny wątek scenerii {name}",
"pragotron-link": "Paletowa tablica informacyjna (beta)",
"tablice-link": "Tablica informacyjna zbiorcza (autorstwa Thundo)",
"bottom-info": "Pokaż pełną historię w zakładce Dziennika"
},
"availability": {
"title": "Dostępność",
"default": "w paczce",
"nonDefault": "poza paczką",
"unavailable": "niedostępna",
"nonPublic": "niepubliczna",
"abandoned": "wycofana"
},
"timetables": {
"timetable-only": "Wyodrębnij rozkłady jazdy",
"end": "Koniec rozkładu jazdy",
"terminated": "Rozkład jazdy zakończony",
"begins": "ROZPOCZYNA\nBIEG",
"terminates": "KOŃCZY BIEG",
"from": "Z",
"to": "DO",
"desc-arriving": "Pociągu nie ma jeszcze na tej scenerii. Przyjedzie z: {prevStationName} (szlak {prevDepartureLine})",
"desc-online": "Pociąg jest na tej scenerii. Odjedzie do: {nextStationName} (szlak {nextArrivalLine})",
"desc-stopped": "Pociąg jest na tej scenerii i odbywa postój. Odjedzie do: {nextStationName} (szlak {nextArrivalLine})",
"desc-next-arrival": "Odjeżdża do: {nextStationName} (szlak {nextArrivalLine})",
"desc-departed": "Pociąg jest na tej scenerii i został odprawiony. Odjeżdża do: {nextStationName} (szlak {nextArrivalLine})",
"desc-departed-away": "Pociąg został odprawiony i odjechał do: {nextStationName} (szlak {nextArrivalLine})",
"desc-end": "Pociąg kończy bieg",
"desc-terminated": "Pociąg skończył bieg"
},
"history": {
"title": "DZIENNIK ROZKŁADÓW JAZDY"
}
}
+22 -4
View File
@@ -9,12 +9,15 @@
"p2-b3": "Stacjobot", "p2-b3": "Stacjobot",
"p2-a1": "serwerze Discord", "p2-a1": "serwerze Discord",
"p3": "<b>Jeśli masz możliwość i chcesz wspomóc moją twórczość dla symulatora, będę wdzięczny za wszelkie pomoce finansowe, które pozwolą na choćby częściowe pokrycie kosztów serwera oraz dalsze rozwijanie możliwości aplikacji!</b>", "p3": "<b>Jeśli masz możliwość i chcesz wspomóc moją twórczość dla symulatora, będę wdzięczny za wszelkie pomoce finansowe, które pozwolą na choćby częściowe pokrycie kosztów serwera oraz dalsze rozwijanie możliwości aplikacji!</b>",
"p4": "Każda osoba, która postanowi przelać co najmniej {b1} na rozwój Stacjownika, otrzyma na życzenie symboliczne {img}{b2} nicku użytkownika w zakładkach \"Scenerie\" i \"Pociągi\" aplikacji i moim serwerze Discord (po zweryfikowaniu autora płatności, najlepiej poprzez podanie nicku bezpośrednio przy niej).", "p4": "Każda osoba, która postanowi przelać co najmniej {b1} (w przypadku PayPala musi być to opłata brutto z dodatkową opłatą za transakcję) na rozwój Stacjownika, otrzyma na życzenie symboliczne {img}{b2} nicku użytkownika na stronie i moim serwerze Discord (po zweryfikowaniu autora płatności, najlepiej poprzez podanie nicku bezpośrednio przy niej).",
"p4-b1": "5 złotych", "p4-b1": "5 złotych",
"p4-b2": "wyróżnienie", "p4-b2": "wyróżnienie",
"p5": "Dzięki i miłego korzystania z aplikacji! <br />~ Spythere", "p5": "Dzięki i miłego korzystania z aplikacji! <br />~ Spythere",
"action-exit": "Może kiedy indziej...", "action-exit": "Może kiedy indziej...",
"action-confirm": "WSPOMÓŻ!" "action-paypal": "PRZELEJ PAYPALEM",
"action-buycoffee": "POSTAW KAWĘ!",
"dispatcher-message": "Dyżurny wspierający projekt Stacjownika!",
"driver-message": "Maszynista wspierający projekt Stacjownika!"
}, },
"general": { "general": {
"and": " oraz ", "and": " oraz ",
@@ -275,6 +278,21 @@
"timeout": "Wystąpił problem z aktualizacją rozkładów jazdy z SWDR" "timeout": "Wystąpił problem z aktualizacją rozkładów jazdy z SWDR"
}, },
"train-stats": {
"stats-button": "STATYSTYKI",
"title": "STATYSTYKI ONLINE",
"timetable-count": "AKTYWNE RJ",
"avg-speed": "ŚREDNIA PRĘDKOŚĆ",
"avg-timetable": "ŚREDNI RJ",
"top-categories": "Kategorie RJ",
"top-vehicles": "Pojazdy w grze",
"top-units": "Najczęstsze jednostki w grze",
"stats-loading": "Ładowanie...",
"no-timetables": "Brak aktywnych rozkładów jazdy na tym serwerze!",
"no-vehicles": "Brak aktywnych pojazdów na tym serwerze!",
"no-units": "Brak aktywnych jednostek na tym serwerze!",
"no-stats": "Brak statystyk online dla wybranego serwera!"
},
"journal": { "journal": {
"title": "HISTORIA DYŻURÓW", "title": "HISTORIA DYŻURÓW",
"loading": "Ładowanie historii dyżurów...", "loading": "Ładowanie historii dyżurów...",
@@ -368,8 +386,8 @@
"two-way-routes": "Szlaki dwutorowe", "two-way-routes": "Szlaki dwutorowe",
"option-active-timetables": "Aktywne rozkłady jazdy", "option-active-timetables": "Aktywne rozkłady jazdy",
"option-timetables-history": "Historia rozkładów", "option-timetables-history": "Historia rozkładów PL1",
"option-dispatchers-history": "Historia dyżurów", "option-dispatchers-history": "Historia dyżurów PL1",
"timetable-author-title": "Wydany przez", "timetable-author-title": "Wydany przez",
"timetable-author-unknown": "Autor nieznany", "timetable-author-unknown": "Autor nieznany",
+16
View File
@@ -0,0 +1,16 @@
import { defineComponent } from 'vue';
import { useApiStore } from '../store/apiStore';
export default defineComponent({
data() {
return {
apiStore: useApiStore()
};
},
methods: {
isDonator(name: string) {
return this.apiStore.donatorsData.includes(name);
}
}
});
+2 -2
View File
@@ -1,10 +1,10 @@
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { useStore } from '../store/mainStore'; import { useMainStore } from '../store/mainStore';
export default defineComponent({ export default defineComponent({
data() { data() {
return { return {
store: useStore() store: useMainStore()
}; };
}, },
+1 -3
View File
@@ -61,12 +61,10 @@ const routes: Array<RouteRecordRaw> = [
const router = createRouter({ const router = createRouter({
scrollBehavior(to, from, savedPosition) { scrollBehavior(to, from, savedPosition) {
if (to.name == 'SceneryView' && from.name && from.query['view'] === undefined) if (to.name == 'SceneryView' && from.name !== to.name && from.query['view'] === undefined)
return { el: `.app_main` }; return { el: `.app_main` };
if (savedPosition) return savedPosition; if (savedPosition) return savedPosition;
// if (from.name == 'SceneryView' && to.name == 'StationsView') return { el: `.last-selected`, top: 20 };
}, },
history: createWebHistory(), history: createWebHistory(),
routes routes
-7
View File
@@ -1,7 +0,0 @@
export const URLs = {
stacjownikAPI:
import.meta.env.VITE_APP_API_DEV === '1' && !import.meta.env.PROD
? 'http://localhost:3001'
: 'https://stacjownik.spythere.eu',
stacjownikAPIDev: 'localhost:3000'
};
+114
View File
@@ -0,0 +1,114 @@
import { defineStore } from 'pinia';
import http from '../http';
import { API } from '../typings/api';
import axios from 'axios';
import { Status } from '../typings/common';
import { StationJSONData } from './typings';
export const useApiStore = defineStore('apiStore', {
state: () => ({
dataStatuses: {
connection: Status.Data.Loading,
sceneries: Status.Data.Loading,
timetables: Status.Data.Loading,
dispatchers: Status.Data.Loading,
trains: Status.Data.Loading
},
activeData: undefined as API.ActiveData.Response | undefined,
rollingStockData: undefined as API.RollingStock.Response | undefined,
donatorsData: [] as API.Donators.Response,
sceneryData: [] as StationJSONData[]
}),
actions: {
async setupAPI() {
// Static data
this.fetchStockInfoData();
this.fetchDonatorsData();
this.fetchStationsGeneralInfo();
this.scheduleFetchActiveData();
},
async setDataStatuses() {
if (!this.activeData?.activeSceneries) {
this.dataStatuses.sceneries = Status.Data.Error;
this.dataStatuses.trains = Status.Data.Error;
this.dataStatuses.dispatchers = Status.Data.Error;
return;
}
this.dataStatuses.sceneries = Status.Data.Loaded;
this.dataStatuses.trains = !this.activeData.trains ? Status.Data.Warning : Status.Data.Loaded;
this.dataStatuses.dispatchers = Status.Data.Loaded;
},
async fetchDonatorsData() {
try {
const response = await http.get<API.Donators.Response>('api/getDonators');
this.donatorsData = response.data;
} catch (error) {
console.error('Ups! Wystąpił błąd podczas pobierania informacji o donatorach:', error);
}
},
async fetchStockInfoData() {
try {
this.rollingStockData = (
await axios.get<API.RollingStock.Response>(
'https://raw.githubusercontent.com/Spythere/api/main/td2/data/stockInfo.json'
)
).data;
} catch (error) {
console.error('Ups! Wystąpił błąd podczas pobierania informacji o taborze z API:', error);
}
},
async scheduleFetchActiveData() {
if (import.meta.env.VITE_API_MODE === 'mock') {
const mockActiveData = await import('../data/mockActiveData.json');
this.dataStatuses.connection = Status.Data.Loaded;
this.activeData = mockActiveData;
this.setDataStatuses();
console.warn('Stacjownik działa w trybie mockowania danych z WS');
return;
}
try {
const data = (await http.get<API.ActiveData.Response>('api/getActiveData')).data;
this.activeData = data;
this.dataStatuses.connection = Status.Data.Loaded;
this.setDataStatuses();
} catch (error) {
this.dataStatuses.connection = Status.Data.Error;
console.error('Wystąpił błąd podczas pobierania danych online z API!');
} finally {
setTimeout(
() => {
this.scheduleFetchActiveData();
},
~~(1000 * (Math.random() * (25 - 20) + 25))
);
}
},
async fetchStationsGeneralInfo() {
const sceneryData: StationJSONData[] = (await http.get<StationJSONData[]>('api/getSceneries'))
.data;
if (!sceneryData) {
this.dataStatuses.sceneries = Status.Data.Error;
return;
}
this.sceneryData = sceneryData;
}
}
});
+95 -133
View File
@@ -1,38 +1,20 @@
import axios from 'axios';
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import { io } from 'socket.io-client';
import StationRoutes from '../scripts/interfaces/StationRoutes'; import StationRoutes from '../scripts/interfaces/StationRoutes';
import Train from '../scripts/interfaces/Train'; import Train from '../scripts/interfaces/Train';
import { URLs } from '../scripts/utils/apiURLs';
import { parseSpawns, getScheduledTrains, getStationTrains } from './utils'; import { parseSpawns, getScheduledTrains, getStationTrains } from './utils';
import { OnlineScenery, ScheduledTrain, StationJSONData, StoreState } from './typings'; import { OnlineScenery, ScheduledTrain, StoreState } from './typings';
import packageInfo from '../../package.json';
import { Websocket, API, GithubAPI } from '../typings/api';
import { Status } from '../typings/common'; import { Status } from '../typings/common';
import Station from '../scripts/interfaces/Station';
import { useApiStore } from './apiStore';
import { API } from '../typings/api';
export const useStore = defineStore('store', { export const useMainStore = defineStore('store', {
state: () => state: () =>
({ ({
activeData: {} as unknown,
rollingStockData: undefined,
donatorsData: [],
stationList: [],
regionOnlineCounters: [],
routesList: [],
sceneryData: [],
lastDispatcherStatuses: [],
region: { id: 'eu', value: 'PL1' }, region: { id: 'eu', value: 'PL1' },
trainCount: 0,
stationCount: 0,
webSocket: undefined,
isOffline: false, isOffline: false,
dispatcherStatsName: '', dispatcherStatsName: '',
@@ -44,31 +26,15 @@ export const useStore = defineStore('store', {
chosenModalTrainId: undefined, chosenModalTrainId: undefined,
dataStatuses: {
connection: Status.Data.Loading,
sceneries: Status.Data.Loading,
timetables: Status.Data.Loading,
dispatchers: Status.Data.Loading,
trains: Status.Data.Loading
},
currentStatsTab: null,
blockScroll: false, blockScroll: false,
listenerLaunched: false, modalLastClickedTarget: null
modalLastClickedTarget: null,
tooltip: {
content: '',
visible: false,
x: 0,
y: 0
}
}) as StoreState, }) as StoreState,
getters: { getters: {
trainList(): Train[] { trainList(): Train[] {
return (this.activeData?.trains ?? []) const apiStore = useApiStore();
return (apiStore.activeData?.trains ?? [])
.filter((train) => train.timetable || train.online) .filter((train) => train.timetable || train.online)
.map((train) => { .map((train) => {
const stock = train.stockString.split(';'); const stock = train.stockString.split(';');
@@ -87,7 +53,7 @@ export const useStore = defineStore('store', {
distance: train.distance, distance: train.distance,
signal: train.signal, signal: train.signal,
online: train.online, online: Boolean(train.online),
driverId: train.driverId, driverId: train.driverId,
driverName: train.driverName, driverName: train.driverName,
currentStationName: train.currentStationName, currentStationName: train.currentStationName,
@@ -119,10 +85,12 @@ export const useStore = defineStore('store', {
}, },
onlineSceneryList(state): OnlineScenery[] { onlineSceneryList(state): OnlineScenery[] {
if (state.isOffline) return []; const apiStore = useApiStore();
if (!state.activeData?.activeSceneries) return [];
return state.activeData?.activeSceneries.reduce((list, scenery) => { if (state.isOffline) return [];
if (!apiStore.activeData?.activeSceneries) return [];
return apiStore.activeData?.activeSceneries.reduce((list, scenery) => {
if (scenery.isOnline !== 1 && Date.now() - scenery.lastSeen > 1000 * 60 * 2) return list; if (scenery.isOnline !== 1 && Date.now() - scenery.lastSeen > 1000 * 60 * 2) return list;
if (scenery.dispatcherStatus == Status.ActiveDispatcher.UNKNOWN) return list; if (scenery.dispatcherStatus == Status.ActiveDispatcher.UNKNOWN) return list;
@@ -181,20 +149,12 @@ export const useStore = defineStore('store', {
return list; return list;
}, [] as OnlineScenery[]); }, [] as OnlineScenery[]);
} },
},
actions: {
async fetchStationsGeneralInfo() {
const sceneryData: StationJSONData[] = await (
await axios.get(`${URLs.stacjownikAPI}/api/getSceneries`)
).data;
if (!sceneryData) { stationList(): Station[] {
this.dataStatuses.sceneries = Status.Data.Error; const apiStore = useApiStore();
return;
}
this.stationList = sceneryData.map((scenery) => { return apiStore.sceneryData.map((scenery) => {
return { return {
name: scenery.name, name: scenery.name,
@@ -243,95 +203,97 @@ export const useStore = defineStore('store', {
} }
}; };
}); });
}, }
},
actions: {
async processStationsOnlineInfo(activeData: API.ActiveData.Response) {
if (!activeData.activeSceneries) return;
async connectToWebsocket() { const onlineSceneries = activeData.activeSceneries.reduce((acc, scenery) => {
if (import.meta.env.VITE_APP_WS_DEV === '1') { const savedStation = this.stationList.find((st) => scenery.stationName === st.name);
const mockWebsocketData = await import('../data/mockWebsocketData.json');
this.dataStatuses.connection = Status.Data.Loaded;
this.activeData = mockWebsocketData as any;
this.setStatuses();
console.warn('Stacjownik działa w trybie mockowania danych z WS'); if (scenery.isOnline !== 1 && Date.now() - scenery.lastSeen > 1000 * 60 * 2) return acc;
if (scenery.dispatcherStatus == Status.ActiveDispatcher.UNKNOWN) return acc;
return; const station = this.stationList.find((s) => s.name === scenery.stationName);
}
const socket = io(URLs.stacjownikAPI, { const scheduledTrains = getScheduledTrains(this.trainList, scenery, station?.generalInfo);
transports: ['websocket', 'polling'],
rememberUpgrade: true,
reconnection: true
});
socket.emit('CONNECTION', { version: packageInfo.version }); const stationTrains = getStationTrains(
this.trainList,
scheduledTrains,
this.region.id,
scenery
);
socket.on('connect_error', () => { // Remove checkpoint duplicates
this.dataStatuses.connection = Status.Data.Error; const uniqueScheduledTrains = scheduledTrains.reduce(
}); (uniqueList, sTrain) =>
uniqueList.find((v) => v.trainId === sTrain.trainId)
? uniqueList
: [...uniqueList, sTrain],
[] as ScheduledTrain[]
);
socket.on('UPDATE', (data: Websocket.ActiveData) => { const dispatcherTimestamp =
this.activeData = data; scenery.dispatcherStatus == Status.ActiveDispatcher.NO_LIMIT
this.dataStatuses.connection = Status.Data.Loaded; ? Date.now() + 25500000
: scenery.dispatcherStatus > 5
? scenery.dispatcherStatus
: null;
this.setStatuses(); const onlineInfo = {
}); name: scenery.stationName,
hash: scenery.stationHash,
region: scenery.region,
maxUsers: scenery.maxUsers,
currentUsers: scenery.currentUsers,
spawns: parseSpawns(scenery.spawnString),
dispatcherName: scenery.dispatcherName,
dispatcherRate: scenery.dispatcherRate,
dispatcherId: scenery.dispatcherId,
dispatcherExp: scenery.dispatcherExp,
dispatcherIsSupporter: scenery.dispatcherIsSupporter,
scheduledTrains: scheduledTrains,
stationTrains: stationTrains,
dispatcherStatus: scenery.dispatcherStatus,
dispatcherTimestamp: dispatcherTimestamp,
socket.emit('FETCH_DATA', { version: packageInfo.version }, (data: Websocket.ActiveData) => { isOnline: scenery.isOnline == 1,
this.dataStatuses.connection = Status.Data.Loaded;
this.activeData = data;
this.setStatuses();
});
this.webSocket = socket; scheduledTrainCount: {
}, all: uniqueScheduledTrains.length,
confirmed: uniqueScheduledTrains.filter((train) => train.stopInfo.confirmed).length,
unconfirmed: uniqueScheduledTrains.filter((train) => !train.stopInfo.confirmed).length
}
};
async connectToAPI() { if (savedStation) savedStation.onlineInfo = onlineInfo;
this.connectToWebsocket(); else
this.fetchStockInfoData(); this.stationList.push({
this.fetchDonatorsData(); name: onlineInfo.name,
this.fetchStationsGeneralInfo(); onlineInfo: onlineInfo
});
acc.push(onlineInfo);
return acc;
}, [] as OnlineScenery[]);
// Reset online info of already offline sceneries
this.stationList
.filter(
(station) =>
station.onlineInfo &&
onlineSceneries.findIndex(
(os) => os.region == station.onlineInfo!.region && station.name == os.name
) != -1
)
.forEach((station) => (station.onlineInfo = undefined));
}, },
async changeRegion(region: StoreState['region']) { async changeRegion(region: StoreState['region']) {
this.region = region; this.region = region;
},
async fetchStockInfoData() {
try {
this.rollingStockData = (
await axios.get<API.RollingStock.Response>(
'https://raw.githubusercontent.com/Spythere/api/main/td2/data/stockInfo.json'
)
).data;
} catch (error) {
console.error('Ups! Wystąpił błąd podczas pobierania informacji o taborze z API:', error);
}
},
async fetchDonatorsData() {
try {
const response = await axios.get<GithubAPI.Donators.Response>(
'https://raw.githubusercontent.com/Spythere/api/main/td2/data/donators.json'
);
if (response.data) this.donatorsData = response.data;
} catch (error) {
console.error('Ups! Wystąpił błąd podczas pobierania informacji o donatorach:', error);
}
},
async setStatuses() {
if (!this.activeData.activeSceneries) {
this.dataStatuses.sceneries = Status.Data.Error;
this.dataStatuses.trains = Status.Data.Error;
this.dataStatuses.dispatchers = Status.Data.Error;
return;
}
this.dataStatuses.sceneries = Status.Data.Loaded;
this.dataStatuses.trains = !this.activeData.trains ? Status.Data.Warning : Status.Data.Loaded;
this.dataStatuses.dispatchers = Status.Data.Loaded;
} }
} }
}); });
+16 -4
View File
@@ -1,6 +1,6 @@
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import inputData from '../data/options.json'; import inputData from '../data/options.json';
import { useStore } from './mainStore'; import { useMainStore } from './mainStore';
import { filterStations, sortStations } from '../scripts/utils/filterUtils'; import { filterStations, sortStations } from '../scripts/utils/filterUtils';
import { HeadIdsTypes } from '../scripts/data/stationHeaderNames'; import { HeadIdsTypes } from '../scripts/data/stationHeaderNames';
import StorageManager from '../managers/storageManager'; import StorageManager from '../managers/storageManager';
@@ -70,14 +70,26 @@ export const useStationFiltersStore = defineStore('stationFiltersStore', {
}, },
filteredStationList: (state) => { filteredStationList: (state) => {
const store = useStore(); const store = useMainStore();
return store.stationList const savedStationNames = store.stationList.map((s) => s.name);
.map((station) => ({
const onlineUnsavedStations = store.onlineSceneryList
.filter((os) => !savedStationNames.includes(os.name) && os.region == store.region.id)
.map((os) => ({
name: os.name,
generalInfo: undefined,
onlineInfo: os
}));
return [
...onlineUnsavedStations,
...store.stationList.map((station) => ({
...station, ...station,
onlineInfo: store.onlineSceneryList.find( onlineInfo: store.onlineSceneryList.find(
(os) => os.name == station.name && os.region == store.region.id (os) => os.name == station.name && os.region == store.region.id
) )
})) }))
]
.filter((station) => filterStations(station, state.filters)) .filter((station) => filterStations(station, state.filters))
.sort((a, b) => sortStations(a, b, state.sorterActive)); .sort((a, b) => sortStations(a, b, state.sorterActive));
} }
+3 -38
View File
@@ -1,6 +1,5 @@
import { Socket } from 'socket.io-client';
import Station from '../scripts/interfaces/Station'; import Station from '../scripts/interfaces/Station';
import { API, GithubAPI, Websocket } from '../typings/api'; import { API } from '../typings/api';
import { Status } from '../typings/common'; import { Status } from '../typings/common';
export type Availability = 'default' | 'unavailable' | 'nonPublic' | 'abandoned' | 'nonDefault'; export type Availability = 'default' | 'unavailable' | 'nonPublic' | 'abandoned' | 'nonDefault';
@@ -12,26 +11,8 @@ export interface RegionCounters {
} }
export interface StoreState { export interface StoreState {
stationList: Station[];
activeData: Websocket.ActiveData;
rollingStockData?: API.RollingStock.Response;
donatorsData: GithubAPI.Donators.Response;
regionOnlineCounters: RegionCounters[];
lastDispatcherStatuses: {
hash: string;
statusTimestamp: number;
statusID: Status.ActiveDispatcher;
}[];
sceneryData: any[][];
region: { id: string; value: string }; region: { id: string; value: string };
trainCount: number;
stationCount: number;
webSocket?: Socket;
isOffline: boolean; isOffline: boolean;
dispatcherStatsName: string; dispatcherStatsName: string;
@@ -43,26 +24,8 @@ export interface StoreState {
chosenModalTrainId?: string; chosenModalTrainId?: string;
currentStatsTab: 'daily' | 'driver' | null;
dataStatuses: {
connection: Status.Data;
sceneries: Status.Data;
timetables: Status.Data;
dispatchers: Status.Data;
trains: Status.Data;
};
listenerLaunched: boolean;
blockScroll: boolean; blockScroll: boolean;
modalLastClickedTarget: EventTarget | null; modalLastClickedTarget: EventTarget | null;
tooltip: {
visible: boolean;
x: number;
y: number;
content: string;
};
} }
export interface StationRoutesInfo { export interface StationRoutesInfo {
@@ -166,6 +129,8 @@ export interface ScheduledTrain {
stopLabel: string; stopLabel: string;
stopStatus: StopStatus; stopStatus: StopStatus;
stopStatusID: number; stopStatusID: number;
region: string;
} }
export enum StopStatus { export enum StopStatus {
+2
View File
@@ -175,6 +175,8 @@ export function getCheckpointTrain(
stopStatus: trainStopStatus.stopStatus, stopStatus: trainStopStatus.stopStatus,
stopStatusID: trainStopStatus.stopStatusID, stopStatusID: trainStopStatus.stopStatusID,
region: train.region,
arrivingLine, arrivingLine,
departureLine, departureLine,
+5 -1
View File
@@ -76,7 +76,11 @@
} }
.train-badge { .train-badge {
padding: 0.1em 0.2em; display: flex;
align-items: center;
gap: 0.5em;
padding: 0.1em 0.3em;
border-radius: 0.2em; border-radius: 0.2em;
font-weight: bold; font-weight: bold;
+47
View File
@@ -0,0 +1,47 @@
@import 'responsive.scss';
@import 'variables.scss';
.dropdown-anim {
&-enter-from,
&-leave-to {
opacity: 0;
transform: translateY(10px);
}
&-enter-active,
&-leave-active {
transition: all 150ms ease;
}
}
.dropdown_background {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
z-index: 10;
}
.dropdown_wrapper {
position: absolute;
left: 0;
top: calc(100% + 0.5em);
background-color: $bgCol;
box-shadow: 0 5px 10px 2px #0f0f0f;
width: 100%;
max-width: 550px;
padding: 1em;
z-index: 100;
}
@include smallScreen {
.dropdown_wrapper {
font-size: 1.1em;
max-width: 100%;
}
}
@@ -1,15 +1,15 @@
@import 'search_box.scss';
@import 'responsive.scss'; @import 'responsive.scss';
@import 'variables.scss'; @import 'variables.scss';
@import 'search_box.scss';
.filters-options {
margin-bottom: 0.5em;
position: relative;
}
.actions-bar { .actions-bar {
display: flex; display: flex;
gap: 0.5em; gap: 0.5em;
margin-bottom: 0.5em;
}
.filters-options {
position: relative;
} }
h1.option-title { h1.option-title {
@@ -29,42 +29,6 @@ h1.option-title {
} }
} }
.options-anim {
&-enter-from,
&-leave-to {
opacity: 0;
transform: translateY(10px);
}
&-enter-active,
&-leave-active {
transition: all 150ms ease;
}
}
.bg {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
z-index: 10;
}
.options_wrapper {
position: absolute;
background-color: $bgCol;
box-shadow: 0 5px 10px 2px #0f0f0f;
width: 100%;
max-width: 550px;
padding: 1em;
z-index: 100;
}
.options_sorters { .options_sorters {
display: flex; display: flex;
align-items: center; align-items: center;
@@ -146,11 +110,6 @@ h1.option-title {
} }
} }
.options_wrapper {
font-size: 1.1em;
max-width: 100%;
}
.options_filters, .options_filters,
.options_sorters { .options_sorters {
justify-content: center; justify-content: center;
+34 -33
View File
@@ -14,7 +14,7 @@
--clr-error: #df3e3e; --clr-error: #df3e3e;
--clr-warning: #c59429; --clr-warning: #c59429;
--clr-honorable: #f47fff; --clr-donator: #f7a4ff;
font-size: 16px; font-size: 16px;
} }
@@ -158,13 +158,14 @@ ul {
color: #ccc; color: #ccc;
} }
&--honorable { &--donator {
color: var(--clr-honorable); color: var(--clr-donator);
text-shadow: var(--clr-honorable) 0 0 10px; text-shadow: var(--clr-donator) 0 0 10px;
} }
} }
button { button,
a.a-button {
cursor: pointer; cursor: pointer;
color: white; color: white;
background: none; background: none;
@@ -184,44 +185,44 @@ button {
pointer-events: none; pointer-events: none;
opacity: 0.85; opacity: 0.85;
} }
}
button.btn--filled { &.btn--filled {
background-color: #1a1a1a; background-color: #1a1a1a;
&:hover { &:hover {
background-color: #2a2a2a; background-color: #2a2a2a;
}
} }
}
button.btn--action { &.btn--action {
background-color: #424242; background-color: #424242;
border-radius: 0.25em; border-radius: 0.25em;
&:hover { &:hover {
background-color: #555; background-color: #555;
}
} }
}
button.btn--option { &.btn--option {
color: white; color: white;
background-color: #333; background-color: #333;
&.checked { &.checked {
color: var(--clr-primary); color: var(--clr-primary);
font-weight: bold;
background-color: #3c3c3c;
}
}
&.btn--image {
font-weight: bold; font-weight: bold;
padding: 0.35em 0.75em;
background-color: #3c3c3c; img {
} width: 1.5em;
} vertical-align: middle;
}
button.btn--image {
font-weight: bold;
padding: 0.35em 0.75em;
img {
width: 1.5em;
vertical-align: middle;
} }
} }
+16 -18
View File
@@ -1,6 +1,12 @@
import { Status } from './common'; import { Status } from './common';
export namespace API { export namespace API {
export namespace ActiveData {
export interface Response {
activeSceneries?: API.ActiveSceneries.Response;
trains?: API.ActiveTrains.Response;
}
}
export namespace DispatcherHistory { export namespace DispatcherHistory {
export type Response = Data[]; export type Response = Data[];
@@ -116,9 +122,9 @@ export namespace API {
driverLevel?: number; driverLevel?: number;
currentStationName: string; currentStationName: string;
currentStationHash: string; currentStationHash?: string;
online: boolean; online: number;
lastSeen: number; lastSeen: number;
region: string; region: string;
@@ -132,16 +138,16 @@ export namespace API {
stopNameRAW: string; stopNameRAW: string;
stopType: string; stopType: string;
stopDistance: number; stopDistance: number;
pointId: number; pointId: string;
mainStop: boolean; mainStop: boolean;
arrivalLine: string; arrivalLine: string | null;
arrivalTimestamp: number; arrivalTimestamp: number;
arrivalRealTimestamp: number; arrivalRealTimestamp: number;
arrivalDelay: number; arrivalDelay: number;
departureLine: string; departureLine: string | null;
departureTimestamp: number; departureTimestamp: number;
departureRealTimestamp: number; departureRealTimestamp: number;
departureDelay: number; departureDelay: number;
@@ -150,9 +156,9 @@ export namespace API {
beginsHere: boolean; beginsHere: boolean;
terminatesHere: boolean; terminatesHere: boolean;
confirmed: boolean; confirmed: number;
stopped: boolean; stopped: number;
stopTime: number; stopTime: number | null;
} }
export interface Timetable { export interface Timetable {
@@ -274,13 +280,9 @@ export namespace API {
}[]; }[];
} }
} }
}
export namespace Websocket { export namespace Donators {
export interface ActiveData { export type Response = string[];
activeSceneries?: API.ActiveSceneries.Response;
trains?: API.ActiveTrains.Response;
connectedSocketCount: number;
} }
} }
@@ -328,8 +330,4 @@ export namespace GithubAPI {
body: string; body: string;
} }
} }
export namespace Donators {
export type Response = string[];
}
} }
+5 -10
View File
@@ -32,11 +32,9 @@
<script lang="ts"> <script lang="ts">
import { defineComponent, provide, reactive, Ref, ref } from 'vue'; import { defineComponent, provide, reactive, Ref, ref } from 'vue';
import axios from 'axios';
import JournalOptions from '../components/JournalView/JournalOptions.vue'; import JournalOptions from '../components/JournalView/JournalOptions.vue';
import { URLs } from '../scripts/utils/apiURLs'; import { useMainStore } from '../store/mainStore';
import { useStore } from '../store/mainStore';
import JournalDispatchersList from '../components/JournalView/JournalDispatchersList.vue'; import JournalDispatchersList from '../components/JournalView/JournalDispatchersList.vue';
import JournalHeader from '../components/JournalView/JournalHeader.vue'; import JournalHeader from '../components/JournalView/JournalHeader.vue';
@@ -44,8 +42,7 @@ import { LocationQuery } from 'vue-router';
import { Journal } from '../components/JournalView/typings'; import { Journal } from '../components/JournalView/typings';
import { API } from '../typings/api'; import { API } from '../typings/api';
import { Status } from '../typings/common'; import { Status } from '../typings/common';
import http from '../http';
const DISPATCHERS_API_URL = `${URLs.stacjownikAPI}/api/getDispatchers`;
export default defineComponent({ export default defineComponent({
components: { components: {
@@ -105,7 +102,7 @@ export default defineComponent({
const scrollElement: Ref<HTMLElement | null> = ref(null); const scrollElement: Ref<HTMLElement | null> = ref(null);
return { return {
store: useStore(), store: useMainStore(),
sorterActive, sorterActive,
searchersValues, searchersValues,
@@ -183,9 +180,7 @@ export default defineComponent({
this.countFromIndex = this.historyList.length; this.countFromIndex = this.historyList.length;
const responseData: API.DispatcherHistory.Response = await ( const responseData: API.DispatcherHistory.Response = await (
await axios.get( await http.get(`api/getDispatchers?${this.currentQuery}&countFrom=${this.countFromIndex}`)
`${DISPATCHERS_API_URL}?${this.currentQuery}&countFrom=${this.countFromIndex}`
)
).data; ).data;
if (!responseData) return; if (!responseData) return;
@@ -232,7 +227,7 @@ export default defineComponent({
if (reset) this.dataStatus = Status.Data.Loading; if (reset) this.dataStatus = Status.Data.Loading;
const responseData: API.DispatcherHistory.Response = await ( const responseData: API.DispatcherHistory.Response = await (
await axios.get(`${DISPATCHERS_API_URL}?${this.currentQuery}`) await http.get(`api/getDispatchers?${this.currentQuery}`)
).data; ).data;
if (!responseData) { if (!responseData) {
+5 -8
View File
@@ -35,7 +35,6 @@
<script lang="ts"> <script lang="ts">
import { defineComponent, provide, reactive, Ref, ref } from 'vue'; import { defineComponent, provide, reactive, Ref, ref } from 'vue';
import axios from 'axios';
import dateMixin from '../mixins/dateMixin'; import dateMixin from '../mixins/dateMixin';
import routerMixin from '../mixins/routerMixin'; import routerMixin from '../mixins/routerMixin';
@@ -45,8 +44,7 @@ import JournalOptions from '../components/JournalView/JournalOptions.vue';
import JournalStats from '../components/JournalView/JournalStats.vue'; import JournalStats from '../components/JournalView/JournalStats.vue';
import JournalHeader from '../components/JournalView/JournalHeader.vue'; import JournalHeader from '../components/JournalView/JournalHeader.vue';
import { URLs } from '../scripts/utils/apiURLs'; import { useMainStore } from '../store/mainStore';
import { useStore } from '../store/mainStore';
import { LocationQuery } from 'vue-router'; import { LocationQuery } from 'vue-router';
@@ -54,8 +52,7 @@ import JournalTimetablesList from '../components/JournalView/JournalTimetables/J
import { Journal } from '../components/JournalView/typings'; import { Journal } from '../components/JournalView/typings';
import { Status } from '../typings/common'; import { Status } from '../typings/common';
import { API } from '../typings/api'; import { API } from '../typings/api';
import http from '../http';
const TIMETABLES_API_URL = `${URLs.stacjownikAPI}/api/getTimetables`;
export const journalTimetableFilters: Journal.TimetableFilter[] = [ export const journalTimetableFilters: Journal.TimetableFilter[] = [
{ {
@@ -194,7 +191,7 @@ export default defineComponent({
scrollElement, scrollElement,
store: useStore() store: useMainStore()
}; };
}, },
@@ -272,7 +269,7 @@ export default defineComponent({
this.currentQueryParams['countFrom'] = this.timetableHistory.length; this.currentQueryParams['countFrom'] = this.timetableHistory.length;
const responseData: API.TimetableHistory.Response = await ( const responseData: API.TimetableHistory.Response = await (
await axios.get(`${TIMETABLES_API_URL}`, { await http.get('api/getTimetables', {
params: { ...this.currentQueryParams } params: { ...this.currentQueryParams }
}) })
).data; ).data;
@@ -365,7 +362,7 @@ export default defineComponent({
try { try {
const responseData: API.TimetableHistory.Response = await ( const responseData: API.TimetableHistory.Response = await (
await axios.get(`${TIMETABLES_API_URL}`, { await http.get('api/getTimetables', {
params: this.currentQueryParams params: this.currentQueryParams
}) })
).data; ).data;
+28 -10
View File
@@ -1,14 +1,17 @@
<template> <template>
<div class="scenery-view"> <div class="scenery-view">
<div class="scenery-offline" v-if="!stationInfo && store.dataStatuses.sceneries == 2"> <!-- <div
class="scenery-offline"
v-if="!stationInfo && !onlineSceneryInfo && store.dataStatuses.sceneries == 2"
>
<div>{{ $t('scenery.no-scenery') }}</div> <div>{{ $t('scenery.no-scenery') }}</div>
<action-button> <action-button>
<router-link to="/">{{ $t('scenery.return-btn') }}</router-link> <router-link to="/">{{ $t('scenery.return-btn') }}</router-link>
</action-button> </action-button>
</div> </div> -->
<div class="scenery-wrapper" v-if="stationInfo" ref="card-wrapper"> <div class="scenery-wrapper" ref="card-wrapper">
<div class="scenery-left"> <div class="scenery-left">
<div class="scenery-actions"> <div class="scenery-actions">
<button class="back-btn btn" :title="$t('scenery.return-btn')" @click="navigateTo('/')"> <button class="back-btn btn" :title="$t('scenery.return-btn')" @click="navigateTo('/')">
@@ -16,7 +19,11 @@
</button> </button>
</div> </div>
<SceneryHeader :station="stationInfo" :onlineScenery="onlineSceneryInfo" /> <SceneryHeader
:stationName="station"
:station="stationInfo"
:onlineScenery="onlineSceneryInfo"
/>
<SceneryInfo :station="stationInfo" :onlineScenery="onlineSceneryInfo" /> <SceneryInfo :station="stationInfo" :onlineScenery="onlineSceneryInfo" />
</div> </div>
@@ -33,7 +40,14 @@
</button> </button>
</div> </div>
<keep-alive> <div
v-if="
apiStore.dataStatuses.sceneries == Status.Loading ||
apiStore.dataStatuses.trains == Status.Loading
"
></div>
<keep-alive v-else>
<component <component
:is="currentMode" :is="currentMode"
:onlineScenery="onlineSceneryInfo" :onlineScenery="onlineSceneryInfo"
@@ -50,7 +64,7 @@
import { computed, defineComponent } from 'vue'; import { computed, defineComponent } from 'vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import routerMixin from '../mixins/routerMixin'; import routerMixin from '../mixins/routerMixin';
import { useStore } from '../store/mainStore'; import { useMainStore } from '../store/mainStore';
import SceneryInfo from '../components/SceneryView/SceneryInfo.vue'; import SceneryInfo from '../components/SceneryView/SceneryInfo.vue';
import SceneryHeader from '../components/SceneryView/SceneryHeader.vue'; import SceneryHeader from '../components/SceneryView/SceneryHeader.vue';
@@ -58,6 +72,8 @@ import SceneryTimetable from '../components/SceneryView/SceneryTimetable.vue';
import SceneryTimetablesHistory from '../components/SceneryView/SceneryTimetablesHistory.vue'; import SceneryTimetablesHistory from '../components/SceneryView/SceneryTimetablesHistory.vue';
import SceneryDispatchersHistory from '../components/SceneryView/SceneryDispatchersHistory.vue'; import SceneryDispatchersHistory from '../components/SceneryView/SceneryDispatchersHistory.vue';
import ActionButton from '../components/Global/ActionButton.vue'; import ActionButton from '../components/Global/ActionButton.vue';
import { Status } from '../typings/common';
import { useApiStore } from '../store/apiStore';
enum SceneryViewMode { enum SceneryViewMode {
'TIMETABLES_ACTIVE', 'TIMETABLES_ACTIVE',
@@ -92,7 +108,9 @@ export default defineComponent({
mixins: [routerMixin], mixins: [routerMixin],
data: () => ({ data: () => ({
store: useStore(), store: useMainStore(),
apiStore: useApiStore(),
viewModes: [ viewModes: [
{ {
id: 'scenery.option-active-timetables', id: 'scenery.option-active-timetables',
@@ -110,7 +128,8 @@ export default defineComponent({
sceneryViewMode: SceneryViewMode, sceneryViewMode: SceneryViewMode,
selectedCheckpoint: '', selectedCheckpoint: '',
currentViewCompontent: 'SceneryTimetable', currentViewCompontent: 'SceneryTimetable',
onlineFrom: -1 onlineFrom: -1,
Status: Status.Data
}), }),
// activated() { // activated() {
@@ -185,8 +204,6 @@ button.back-btn {
&-view { &-view {
display: flex; display: flex;
justify-content: center; justify-content: center;
min-height: 100vh;
} }
&-offline { &-offline {
@@ -215,6 +232,7 @@ button.back-btn {
width: 100%; width: 100%;
max-width: 1700px; max-width: 1700px;
min-height: 100vh;
margin: 1rem 0; margin: 1rem 0;
text-align: center; text-align: center;
+2 -2
View File
@@ -21,7 +21,7 @@ import { defineComponent } from 'vue';
import StationTable from '../components/StationsView/StationTable.vue'; import StationTable from '../components/StationsView/StationTable.vue';
import StationFilterCard from '../components/StationsView/StationFilterCard.vue'; import StationFilterCard from '../components/StationsView/StationFilterCard.vue';
import { useStationFiltersStore } from '../store/stationFiltersStore'; import { useStationFiltersStore } from '../store/stationFiltersStore';
import { useStore } from '../store/mainStore'; import { useMainStore } from '../store/mainStore';
import Donation from '../components/Global/Donation.vue'; import Donation from '../components/Global/Donation.vue';
export default defineComponent({ export default defineComponent({
@@ -37,7 +37,7 @@ export default defineComponent({
STORAGE_KEY: 'options_saved', STORAGE_KEY: 'options_saved',
focusedStationName: '', focusedStationName: '',
filterStore: useStationFiltersStore(), filterStore: useStationFiltersStore(),
store: useStore(), store: useMainStore(),
isDonationModalOpen: false isDonationModalOpen: false
}), }),
+24 -8
View File
@@ -1,10 +1,14 @@
<template> <template>
<section class="trains-view"> <section class="trains-view">
<div class="trains_wrapper"> <div class="trains_wrapper">
<TrainOptions <div class="trains_topbar">
:sorter-option-ids="['routeDistance', 'id', 'progress', 'delay', 'mass', 'speed', 'length']" <TrainOptions
:current-options-active="currentOptionsActive" :sorter-option-ids="sorterIds"
/> :current-options-active="currentOptionsActive"
/>
<TrainStats />
</div>
<TrainTable :trains="computedTrains" /> <TrainTable :trains="computedTrains" />
</div> </div>
@@ -17,14 +21,16 @@ import TrainOptions from '../components/TrainsView/TrainOptions.vue';
import TrainTable from '../components/TrainsView/TrainTable.vue'; import TrainTable from '../components/TrainsView/TrainTable.vue';
import modalTrainMixin from '../mixins/modalTrainMixin'; import modalTrainMixin from '../mixins/modalTrainMixin';
import Train from '../scripts/interfaces/Train'; import Train from '../scripts/interfaces/Train';
import { useStore } from '../store/mainStore'; import { useMainStore } from '../store/mainStore';
import { TrainFilter, trainFilters } from '../components/TrainsView/typings'; import { TrainFilter, trainFilters } from '../components/TrainsView/typings';
import { filteredTrainList } from '../managers/trainFilterManager'; import { filteredTrainList } from '../managers/trainFilterManager';
import TrainStats from '../components/TrainsView/TrainStats.vue';
export default defineComponent({ export default defineComponent({
components: { components: {
TrainTable, TrainTable,
TrainOptions TrainOptions,
TrainStats
}, },
mixins: [modalTrainMixin], mixins: [modalTrainMixin],
@@ -47,11 +53,12 @@ export default defineComponent({
}, },
data: () => ({ data: () => ({
trainStatsOpen: false trainStatsOpen: false,
sorterIds: ['routeDistance', 'id', 'progress', 'delay', 'mass', 'speed', 'length']
}), }),
setup() { setup() {
const store = useStore(); const store = useMainStore();
const initTrainFilters = [...trainFilters.map((f) => ({ ...f }))]; const initTrainFilters = [...trainFilters.map((f) => ({ ...f }))];
const sorterActive = reactive({ id: 'routeDistance', dir: -1 }); const sorterActive = reactive({ id: 'routeDistance', dir: -1 });
@@ -121,4 +128,13 @@ export default defineComponent({
margin: 1rem auto; margin: 1rem auto;
max-width: 1350px; max-width: 1350px;
} }
.trains_topbar {
display: flex;
align-items: center;
gap: 0.5em;
position: relative;
margin-bottom: 0.5em;
}
</style> </style>
+93 -164
View File
@@ -29,12 +29,13 @@
jsonpointer "^5.0.0" jsonpointer "^5.0.0"
leven "^3.1.0" leven "^3.1.0"
"@babel/code-frame@^7.10.4", "@babel/code-frame@^7.18.6": "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.18.6", "@babel/code-frame@^7.22.13", "@babel/code-frame@^7.23.5":
version "7.18.6" version "7.23.5"
resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz" resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz"
integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q== integrity sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==
dependencies: dependencies:
"@babel/highlight" "^7.18.6" "@babel/highlight" "^7.23.4"
chalk "^2.4.2"
"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.1", "@babel/compat-data@^7.20.5": "@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.1", "@babel/compat-data@^7.20.5":
version "7.20.10" version "7.20.10"
@@ -62,13 +63,14 @@
json5 "^2.2.1" json5 "^2.2.1"
semver "^6.3.0" semver "^6.3.0"
"@babel/generator@^7.20.7": "@babel/generator@^7.20.7", "@babel/generator@^7.23.5":
version "7.20.7" version "7.23.5"
resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.20.7.tgz" resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.23.5.tgz"
integrity sha512-7wqMOJq8doJMZmP4ApXTzLxSr7+oO2jroJURrVEp6XShrQUObV8Tq/D0NCcoYg2uHqUrjzO0zwBjoYzelxK+sw== integrity sha512-BPssCHrBD+0YrxviOa3QzpqwhNIXKEtOa2jQrm4FlmkC2apYgRnQcmPWiGZDlGxiNtltnUFolMe8497Esry+jA==
dependencies: dependencies:
"@babel/types" "^7.20.7" "@babel/types" "^7.23.5"
"@jridgewell/gen-mapping" "^0.3.2" "@jridgewell/gen-mapping" "^0.3.2"
"@jridgewell/trace-mapping" "^0.3.17"
jsesc "^2.5.1" jsesc "^2.5.1"
"@babel/helper-annotate-as-pure@^7.18.6": "@babel/helper-annotate-as-pure@^7.18.6":
@@ -130,10 +132,10 @@
resolve "^1.14.2" resolve "^1.14.2"
semver "^6.1.2" semver "^6.1.2"
"@babel/helper-environment-visitor@^7.18.9": "@babel/helper-environment-visitor@^7.18.9", "@babel/helper-environment-visitor@^7.22.20":
version "7.18.9" version "7.22.20"
resolved "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz" resolved "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz"
integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==
"@babel/helper-explode-assignable-expression@^7.18.6": "@babel/helper-explode-assignable-expression@^7.18.6":
version "7.18.6" version "7.18.6"
@@ -142,20 +144,20 @@
dependencies: dependencies:
"@babel/types" "^7.18.6" "@babel/types" "^7.18.6"
"@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.19.0": "@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.19.0", "@babel/helper-function-name@^7.23.0":
version "7.19.0" version "7.23.0"
resolved "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz" resolved "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz"
integrity sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w== integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==
dependencies: dependencies:
"@babel/template" "^7.18.10" "@babel/template" "^7.22.15"
"@babel/types" "^7.19.0" "@babel/types" "^7.23.0"
"@babel/helper-hoist-variables@^7.18.6": "@babel/helper-hoist-variables@^7.18.6", "@babel/helper-hoist-variables@^7.22.5":
version "7.18.6" version "7.22.5"
resolved "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz" resolved "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz"
integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q== integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==
dependencies: dependencies:
"@babel/types" "^7.18.6" "@babel/types" "^7.22.5"
"@babel/helper-member-expression-to-functions@^7.20.7": "@babel/helper-member-expression-to-functions@^7.20.7":
version "7.20.7" version "7.20.7"
@@ -233,22 +235,22 @@
dependencies: dependencies:
"@babel/types" "^7.20.0" "@babel/types" "^7.20.0"
"@babel/helper-split-export-declaration@^7.18.6": "@babel/helper-split-export-declaration@^7.18.6", "@babel/helper-split-export-declaration@^7.22.6":
version "7.18.6" version "7.22.6"
resolved "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz" resolved "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz"
integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA== integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==
dependencies: dependencies:
"@babel/types" "^7.18.6" "@babel/types" "^7.22.5"
"@babel/helper-string-parser@^7.19.4": "@babel/helper-string-parser@^7.23.4":
version "7.19.4" version "7.23.4"
resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz" resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz"
integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw== integrity sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==
"@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": "@babel/helper-validator-identifier@^7.19.1", "@babel/helper-validator-identifier@^7.22.20":
version "7.19.1" version "7.22.20"
resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz" resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz"
integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==
"@babel/helper-validator-option@^7.18.6": "@babel/helper-validator-option@^7.18.6":
version "7.18.6" version "7.18.6"
@@ -274,29 +276,19 @@
"@babel/traverse" "^7.20.7" "@babel/traverse" "^7.20.7"
"@babel/types" "^7.20.7" "@babel/types" "^7.20.7"
"@babel/highlight@^7.18.6": "@babel/highlight@^7.23.4":
version "7.18.6" version "7.23.4"
resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz" resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz"
integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== integrity sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==
dependencies: dependencies:
"@babel/helper-validator-identifier" "^7.18.6" "@babel/helper-validator-identifier" "^7.22.20"
chalk "^2.0.0" chalk "^2.4.2"
js-tokens "^4.0.0" js-tokens "^4.0.0"
"@babel/parser@^7.20.15": "@babel/parser@^7.20.15", "@babel/parser@^7.20.7", "@babel/parser@^7.21.3", "@babel/parser@^7.22.15", "@babel/parser@^7.23.5":
version "7.22.16" version "7.23.5"
resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.22.16.tgz" resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.23.5.tgz"
integrity sha512-+gPfKv8UWeKKeJTUxe59+OobVcrYHETCsORl61EmSkmgymguYk/X5bp7GuUIXaFsc6y++v8ZxPsLSSuujqDphA== integrity sha512-hOOqoiNXrmGdFbhgCzu6GiURxUgM27Xwd/aPuu8RfHEZPBzL1Z54okAHAQjXfcQNwvrlkAmAp4SlRTZ45vlthQ==
"@babel/parser@^7.20.7":
version "7.20.7"
resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.20.7.tgz"
integrity sha512-T3Z9oHybU+0vZlY9CiDSJQTD5ZapcW18ZctFMi0MOAl/4BjFF4ul7NVSARLdbGO5vDqy9eQiGTV0LtKfvCYvcg==
"@babel/parser@^7.21.3":
version "7.22.16"
resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.22.16.tgz"
integrity sha512-+gPfKv8UWeKKeJTUxe59+OobVcrYHETCsORl61EmSkmgymguYk/X5bp7GuUIXaFsc6y++v8ZxPsLSSuujqDphA==
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6": "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6":
version "7.18.6" version "7.18.6"
@@ -898,38 +890,38 @@
dependencies: dependencies:
regenerator-runtime "^0.13.11" regenerator-runtime "^0.13.11"
"@babel/template@^7.18.10", "@babel/template@^7.20.7": "@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.22.15":
version "7.20.7" version "7.22.15"
resolved "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz" resolved "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz"
integrity sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw== integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==
dependencies: dependencies:
"@babel/code-frame" "^7.18.6" "@babel/code-frame" "^7.22.13"
"@babel/parser" "^7.20.7" "@babel/parser" "^7.22.15"
"@babel/types" "^7.20.7" "@babel/types" "^7.22.15"
"@babel/traverse@^7.20.5", "@babel/traverse@^7.20.7": "@babel/traverse@^7.20.5", "@babel/traverse@^7.20.7":
version "7.20.10" version "7.23.5"
resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.10.tgz" resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.5.tgz"
integrity sha512-oSf1juCgymrSez8NI4A2sr4+uB/mFd9MXplYGPEBnfAuWmmyeVcHa6xLPiaRBcXkcb/28bgxmQLTVwFKE1yfsg== integrity sha512-czx7Xy5a6sapWWRx61m1Ke1Ra4vczu1mCTtJam5zRTBOonfdJ+S/B6HYmGYu3fJtr8GGET3si6IhgWVBhJ/m8w==
dependencies: dependencies:
"@babel/code-frame" "^7.18.6" "@babel/code-frame" "^7.23.5"
"@babel/generator" "^7.20.7" "@babel/generator" "^7.23.5"
"@babel/helper-environment-visitor" "^7.18.9" "@babel/helper-environment-visitor" "^7.22.20"
"@babel/helper-function-name" "^7.19.0" "@babel/helper-function-name" "^7.23.0"
"@babel/helper-hoist-variables" "^7.18.6" "@babel/helper-hoist-variables" "^7.22.5"
"@babel/helper-split-export-declaration" "^7.18.6" "@babel/helper-split-export-declaration" "^7.22.6"
"@babel/parser" "^7.20.7" "@babel/parser" "^7.23.5"
"@babel/types" "^7.20.7" "@babel/types" "^7.23.5"
debug "^4.1.0" debug "^4.1.0"
globals "^11.1.0" globals "^11.1.0"
"@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.5", "@babel/types@^7.20.7", "@babel/types@^7.4.4": "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.5", "@babel/types@^7.20.7", "@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.5", "@babel/types@^7.4.4":
version "7.20.7" version "7.23.5"
resolved "https://registry.npmjs.org/@babel/types/-/types-7.20.7.tgz" resolved "https://registry.npmjs.org/@babel/types/-/types-7.23.5.tgz"
integrity sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg== integrity sha512-ON5kSOJwVO6xXVRTvOI0eOnWe7VdUcIpsovGo9U/Br4Ie4UVFQTboO2cYnDhAGU6Fp+UxSiT+pMft0SMHfuq6w==
dependencies: dependencies:
"@babel/helper-string-parser" "^7.19.4" "@babel/helper-string-parser" "^7.23.4"
"@babel/helper-validator-identifier" "^7.19.1" "@babel/helper-validator-identifier" "^7.22.20"
to-fast-properties "^2.0.0" to-fast-properties "^2.0.0"
"@canvas/image-data@^1.0.0": "@canvas/image-data@^1.0.0":
@@ -1449,7 +1441,7 @@
resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz" resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz"
integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==
"@jridgewell/trace-mapping@^0.3.9": "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9":
version "0.3.17" version "0.3.17"
resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz" resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz"
integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g== integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==
@@ -1585,11 +1577,6 @@
resolved "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.5.1.tgz" resolved "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.5.1.tgz"
integrity sha512-6i/8UoL0P5y4leBIGzvkZdS85RDMG9y1ihZzmTZQ5LdHUYmZ7pKFoj8X0236s3lusPs1Fa5HTQUpwI+UfTcmeA== integrity sha512-6i/8UoL0P5y4leBIGzvkZdS85RDMG9y1ihZzmTZQ5LdHUYmZ7pKFoj8X0236s3lusPs1Fa5HTQUpwI+UfTcmeA==
"@socket.io/component-emitter@~3.1.0":
version "3.1.0"
resolved "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz"
integrity sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==
"@surma/rollup-plugin-off-main-thread@^2.2.3": "@surma/rollup-plugin-off-main-thread@^2.2.3":
version "2.2.3" version "2.2.3"
resolved "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz" resolved "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz"
@@ -1997,9 +1984,9 @@ at-least-node@^1.0.0:
integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==
axios@^1.5.0: axios@^1.5.0:
version "1.5.0" version "1.6.2"
resolved "https://registry.npmjs.org/axios/-/axios-1.5.0.tgz" resolved "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz"
integrity sha512-D4DdjDo5CY50Qms0qGQTTw6Q44jl7zRwY7bthds06pUGfChBCTcQs+N743eFWGEd6pRTMd6A+I87aWyFV5wiZQ== integrity sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==
dependencies: dependencies:
follow-redirects "^1.15.0" follow-redirects "^1.15.0"
form-data "^4.0.0" form-data "^4.0.0"
@@ -2151,11 +2138,11 @@ callsites@^3.0.0:
integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
caniuse-lite@^1.0.30001400: caniuse-lite@^1.0.30001400:
version "1.0.30001503" version "1.0.30001565"
resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001503.tgz" resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001565.tgz"
integrity sha512-Sf9NiF+wZxPfzv8Z3iS0rXM1Do+iOy2Lxvib38glFX+08TCYYYGR5fRJXk4d77C4AYwhUjgYgMsMudbh2TqCKw== integrity sha512-xrE//a3O7TP0vaJ8ikzkD2c2NgcVUvsEe2IvFTntV4Yd1Z9FVzh+gW+enX96L0psrbaFMcVcH2l90xNuGDWc8w==
chalk@^2.0.0: chalk@^2.4.2:
version "2.4.2" version "2.4.2"
resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz"
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
@@ -2327,7 +2314,7 @@ de-indent@^1.0.2:
resolved "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz" resolved "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz"
integrity sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg== integrity sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==
debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4, debug@~4.3.1, debug@~4.3.2: debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4:
version "4.3.4" version "4.3.4"
resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz" resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz"
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
@@ -2462,22 +2449,6 @@ end-of-stream@^1.1.0, end-of-stream@^1.4.1:
dependencies: dependencies:
once "^1.4.0" once "^1.4.0"
engine.io-client@~6.5.2:
version "6.5.2"
resolved "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.2.tgz"
integrity sha512-CQZqbrpEYnrpGqC07a9dJDz4gePZUgTPMU3NKJPSeQOyw27Tst4Pl3FemKoFGAlHzgZmKjoRmiJvbWfhCXUlIg==
dependencies:
"@socket.io/component-emitter" "~3.1.0"
debug "~4.3.1"
engine.io-parser "~5.2.1"
ws "~8.11.0"
xmlhttprequest-ssl "~2.0.0"
engine.io-parser@~5.2.1:
version "5.2.1"
resolved "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.1.tgz"
integrity sha512-9JktcM3u18nU9N2Lz3bWeBgxVgOKpw7yhRaoxQA3FUDZzzw+9WlA6p4G4u0RixNkg14fH7EfEc/RhpurtiROTQ==
es-abstract@^1.19.0, es-abstract@^1.20.4: es-abstract@^1.19.0, es-abstract@^1.20.4:
version "1.20.5" version "1.20.5"
resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.5.tgz" resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.5.tgz"
@@ -3597,11 +3568,6 @@ muggle-string@^0.3.1:
resolved "https://registry.npmjs.org/muggle-string/-/muggle-string-0.3.1.tgz" resolved "https://registry.npmjs.org/muggle-string/-/muggle-string-0.3.1.tgz"
integrity sha512-ckmWDJjphvd/FvZawgygcUeQCxzvohjFO5RxTjj4eq8kw359gFF3E1brjfI+viLMxss5JrHTDRHZvu2/tuy0Qg== integrity sha512-ckmWDJjphvd/FvZawgygcUeQCxzvohjFO5RxTjj4eq8kw359gFF3E1brjfI+viLMxss5JrHTDRHZvu2/tuy0Qg==
nanoid@^3.3.4:
version "3.3.4"
resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz"
integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==
nanoid@^3.3.6: nanoid@^3.3.6:
version "3.3.6" version "3.3.6"
resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz" resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz"
@@ -3821,19 +3787,10 @@ postcss-selector-parser@^6.0.13:
cssesc "^3.0.0" cssesc "^3.0.0"
util-deprecate "^1.0.2" util-deprecate "^1.0.2"
postcss@^8.1.10: postcss@^8.1.10, postcss@^8.4.27:
version "8.4.20" version "8.4.31"
resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.20.tgz" resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz"
integrity sha512-6Q04AXR1212bXr5fh03u8aAwbLxAQNGQ/Q1LNa0VfOI06ZAlhPHtQvE4OIdpj4kLThXilalPnmDSOD65DcHt+g== integrity sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==
dependencies:
nanoid "^3.3.4"
picocolors "^1.0.0"
source-map-js "^1.0.2"
postcss@^8.4.27:
version "8.4.30"
resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.30.tgz"
integrity sha512-7ZEao1g4kd68l97aWG/etQKPKq07us0ieSZ2TnFDk11i0ZfDW2AwKHYU8qv4MZKqN2fdBfg+7q0ES06UA73C1g==
dependencies: dependencies:
nanoid "^3.3.6" nanoid "^3.3.6"
picocolors "^1.0.0" picocolors "^1.0.0"
@@ -4123,9 +4080,9 @@ sass@*, sass@^1.67.0:
source-map-js ">=0.6.2 <2.0.0" source-map-js ">=0.6.2 <2.0.0"
semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: semver@^6.1.1, semver@^6.1.2, semver@^6.3.0:
version "6.3.0" version "6.3.1"
resolved "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz" resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz"
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
semver@^7.3.5: semver@^7.3.5:
version "7.5.4" version "7.5.4"
@@ -4237,24 +4194,6 @@ slash@^3.0.0:
resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz" resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz"
integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
socket.io-client@^4.7.2:
version "4.7.2"
resolved "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.2.tgz"
integrity sha512-vtA0uD4ibrYD793SOIAwlo8cj6haOeMHrGvwPxJsxH7CeIksqJ+3Zc06RvWTIFgiSqx4A3sOnTXpfAEE2Zyz6w==
dependencies:
"@socket.io/component-emitter" "~3.1.0"
debug "~4.3.2"
engine.io-client "~6.5.2"
socket.io-parser "~4.2.4"
socket.io-parser@~4.2.4:
version "4.2.4"
resolved "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz"
integrity sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==
dependencies:
"@socket.io/component-emitter" "~3.1.0"
debug "~4.3.1"
source-map-js@^1.0.2, "source-map-js@>=0.6.2 <2.0.0": source-map-js@^1.0.2, "source-map-js@>=0.6.2 <2.0.0":
version "1.0.2" version "1.0.2"
resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz" resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz"
@@ -4653,9 +4592,9 @@ vite-plugin-pwa@^0.16.5:
workbox-window "^7.0.0" workbox-window "^7.0.0"
"vite@^3.1.0 || ^4.0.0", vite@^4.0.0, vite@^4.4.9: "vite@^3.1.0 || ^4.0.0", vite@^4.0.0, vite@^4.4.9:
version "4.4.9" version "4.5.1"
resolved "https://registry.npmjs.org/vite/-/vite-4.4.9.tgz" resolved "https://registry.npmjs.org/vite/-/vite-4.5.1.tgz"
integrity sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA== integrity sha512-AXXFaAJ8yebyqzoNB9fu2pHoo/nWX+xZlaRwoeYUxEqBO+Zj4msE5G+BhGBll9lYEKv9Hfks52PAF2X7qDYXQA==
dependencies: dependencies:
esbuild "^0.18.10" esbuild "^0.18.10"
postcss "^8.4.27" postcss "^8.4.27"
@@ -4956,21 +4895,11 @@ wrappy@1:
resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz"
integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
ws@~8.11.0:
version "8.11.0"
resolved "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz"
integrity sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==
xml-name-validator@^4.0.0: xml-name-validator@^4.0.0:
version "4.0.0" version "4.0.0"
resolved "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz" resolved "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz"
integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw== integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==
xmlhttprequest-ssl@~2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz"
integrity sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==
y18n@^5.0.5: y18n@^5.0.5:
version "5.0.8" version "5.0.8"
resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz" resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz"