Poprawki błędów (1.3.5)

This commit is contained in:
2020-12-29 01:48:10 +01:00
parent ff17b791f0
commit 709eacc980
33 changed files with 7037 additions and 7039 deletions
+40 -40
View File
@@ -1,40 +1,40 @@
{ {
"name": "stacjownik", "name": "stacjownik",
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"scripts": { "scripts": {
"serve": "vue-cli-service serve", "serve": "vue-cli-service serve",
"build": "vue-cli-service build", "build": "vue-cli-service build",
"deploy": "npm run build && firebase deploy --only hosting" "deploy": "npm run build && firebase deploy --only hosting"
}, },
"dependencies": { "dependencies": {
"core-js": "^3.6.5", "core-js": "^3.6.5",
"dotenv": "^8.2.0", "dotenv": "^8.2.0",
"firestore": "^1.1.6", "firestore": "^1.1.6",
"howler": "^2.2.1", "howler": "^2.2.1",
"vue": "^2.6.11", "vue": "^2.6.11",
"vue-class-component": "^7.2.5", "vue-class-component": "^7.2.5",
"vue-property-decorator": "^8.4.2", "vue-property-decorator": "^8.4.2",
"vue-router": "^3.4.3", "vue-router": "^3.4.3",
"vuex": "^3.4.0" "vuex": "^3.4.0"
}, },
"devDependencies": { "devDependencies": {
"@vue/cli-plugin-babel": "~4.4.0", "@vue/cli-plugin-babel": "~4.4.0",
"@vue/cli-plugin-router": "~4.4.0", "@vue/cli-plugin-router": "~4.4.0",
"@vue/cli-plugin-typescript": "~4.4.0", "@vue/cli-plugin-typescript": "~4.4.0",
"@vue/cli-plugin-vuex": "~4.4.0", "@vue/cli-plugin-vuex": "~4.4.0",
"@vue/cli-service": "~4.4.0", "@vue/cli-service": "~4.4.0",
"axios": "^0.19.2", "axios": "^0.19.2",
"sass": "^1.26.10", "sass": "^1.26.10",
"sass-loader": "^8.0.2", "sass-loader": "^8.0.2",
"typescript": "^3.9.7", "typescript": "^3.9.7",
"vue-template-compiler": "^2.6.11", "vue-template-compiler": "^2.6.11",
"vuex-class": "^0.3.2", "vuex-class": "^0.3.2",
"vuex-module-decorators": "^0.17.0" "vuex-module-decorators": "^0.17.0"
}, },
"browserslist": [ "browserslist": [
"> 1%", "> 1%",
"last 2 versions", "last 2 versions",
"not dead" "not dead"
] ]
} }
+4 -4
View File
@@ -1,4 +1,4 @@
<svg width="26" height="34" viewBox="0 0 26 34" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="26" height="34" viewBox="0 0 26 34" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1 14L13 2L25 14" stroke="white" stroke-width="2"/> <path d="M1 14L13 2L25 14" stroke="white" stroke-width="2"/>
<path d="M1 20L13 32L25 20" stroke="white" stroke-width="2"/> <path d="M1 20L13 32L25 20" stroke="white" stroke-width="2"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 226 B

After

Width:  |  Height:  |  Size: 230 B

+165 -165
View File
@@ -1,166 +1,166 @@
<template> <template>
<div class="modal" v-if="!modalHidden"> <div class="modal" v-if="!modalHidden">
<div class="modal_content"> <div class="modal_content">
<span class="modal_title">Grosza daj Stacjownikowi...</span> <span class="modal_title">Grosza daj Stacjownikowi...</span>
<div class="modal_body"> <div class="modal_body">
<div class="modal_body-header"> <div class="modal_body-header">
Stacjownik to projekt całkowicie darmowy dla wszystkich. Stacjownik to projekt całkowicie darmowy dla wszystkich.
Jednak jeśli chcesz go wesprzeć i pomóc w rozwoju strony oraz nowych funkcjonalności, które wykraczają poza darmowe możliwości Jednak jeśli chcesz go wesprzeć i pomóc w rozwoju strony oraz nowych funkcjonalności, które wykraczają poza darmowe możliwości
hostingu, na którym jest postawiony, zostaw złotówkę, nowigradzką koronę czy nawet rubla! hostingu, na którym jest postawiony, zostaw złotówkę, nowigradzką koronę czy nawet rubla!
</div> </div>
<div class="modal_payments"> <div class="modal_payments">
<div>Płatności dokonasz korzystając z poniższych metod:</div> <div>Płatności dokonasz korzystając z poniższych metod:</div>
<div class="payment"> <div class="payment">
<div> <div>
<a target="_blank" href="https://paypal.me/spythere"> <a target="_blank" href="https://paypal.me/spythere">
<img :src="paypalIcon" alt="icon-paypal" /> <img :src="paypalIcon" alt="icon-paypal" />
<span>PAYPAL</span> <span>PAYPAL</span>
</a> </a>
</div> </div>
</div> </div>
<div class="payment"> <div class="payment">
<div> <div>
<div class="payment_open" v-if="showNumber">94 1140 2004 0000 3502 7784 9203</div> <div class="payment_open" v-if="showNumber">94 1140 2004 0000 3502 7784 9203</div>
<div class="payment_closed" v-else @click="showNumber = true"> <div class="payment_closed" v-else @click="showNumber = true">
<b>PRZELEW NA KONTO</b> <b>PRZELEW NA KONTO</b>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div>Wielkie dzięki i do zobaczenia na szlaku!</div> <div>Wielkie dzięki i do zobaczenia na szlaku!</div>
<div class="modal_buttons"> <div class="modal_buttons">
<button class="button" @click="toggleModal">PRZYJĄŁEM!</button> <button class="button" @click="toggleModal">PRZYJĄŁEM!</button>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { Component, Vue, Prop } from "vue-property-decorator"; import { Component, Vue, Prop } from "vue-property-decorator";
import StorageManager from "@/scripts/storageManager"; import StorageManager from "@/scripts/storageManager";
@Component @Component
export default class Modal extends Vue { export default class Modal extends Vue {
@Prop() modalHidden!: boolean; @Prop() modalHidden!: boolean;
showNumber = false; showNumber = false;
STORAGE_ID = "modal_donation"; STORAGE_ID = "modal_donation";
paypalIcon: string = require("@/assets/icon-paypal.svg"); paypalIcon: string = require("@/assets/icon-paypal.svg");
toggleModal(type: string) { toggleModal(type: string) {
this.$emit("toggleModal"); this.$emit("toggleModal");
} }
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import "../../styles/responsive"; @import "../../styles/responsive";
.modal { .modal {
z-index: 100; z-index: 100;
font-size: calc(1rem + 0.8vw); font-size: calc(1rem + 0.8vw);
padding: 0.3rem; padding: 0.3rem;
border-radius: 1em; border-radius: 1em;
position: fixed; position: fixed;
top: 50%; top: 50%;
left: 50%; left: 50%;
width: 65%; width: 65%;
max-width: 950px; max-width: 950px;
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
background: rgba(black, 0.85); background: rgba(black, 0.85);
color: white; color: white;
@include bigScreen() { @include bigScreen() {
font-size: 2rem; font-size: 2rem;
} }
@include smallScreen() { @include smallScreen() {
font-size: 1.2rem; font-size: 1.2rem;
width: 95%; width: 95%;
} }
&_content { &_content {
margin: 0 auto; margin: 0 auto;
text-align: center; text-align: center;
padding: 0.5em; padding: 0.5em;
} }
&_title { &_title {
color: gold; color: gold;
font-weight: bold; font-weight: bold;
} }
&_body { &_body {
font-size: 0.75em; font-size: 0.75em;
&-header { &-header {
text-align: justify; text-align: justify;
} }
> div { > div {
margin-top: 0.5em; margin-top: 0.5em;
} }
} }
&_payments { &_payments {
> span { > span {
margin-right: 0.5em; margin-right: 0.5em;
} }
.payment { .payment {
display: flex; display: flex;
justify-content: center; justify-content: center;
margin-top: 0.3em; margin-top: 0.3em;
&_closed { &_closed {
cursor: pointer; cursor: pointer;
&:hover { &:hover {
color: gold; color: gold;
} }
} }
a { a {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
border-radius: 0.2em; border-radius: 0.2em;
padding: 0.15em 0.3em; padding: 0.15em 0.3em;
font-size: 1.1em; font-size: 1.1em;
font-weight: bold; font-weight: bold;
} }
img { img {
width: 1.2em; width: 1.2em;
margin-right: 0.2em; margin-right: 0.2em;
} }
} }
} }
&_buttons { &_buttons {
display: flex; display: flex;
justify-content: center; justify-content: center;
> button { > button {
margin: 0 0.5em; margin: 0 0.5em;
} }
} }
} }
</style> </style>
+108 -108
View File
@@ -1,109 +1,109 @@
<template> <template>
<div class="select-box"> <div class="select-box">
<div class="title">Sortuj według</div> <div class="title">Sortuj według</div>
<div class="option-selected" @click="toggleOptionList"> <div class="option-selected" @click="toggleOptionList">
<span>{{ selectedOption }}</span> <span>{{ selectedOption }}</span>
<img :src="require('@/assets/icon-select.svg')" alt="icon-select" /> <img :src="require('@/assets/icon-select.svg')" alt="icon-select" />
</div> </div>
<div class="option-container"> <div class="option-container">
<ul class="option-list" :class="{ open: listOpen }"> <ul class="option-list" :class="{ open: listOpen }">
<li <li
class="option-item" class="option-item"
v-for="(option, i) in sortOptionList" v-for="(option, i) in sortOptionList"
:key="i" :key="i"
@click="() => chooseOption(option)" @click="() => chooseOption(option)"
> >
<input type="option-radio" name="sort" :id="option.id" /> <input type="option-radio" name="sort" :id="option.id" />
<label :for="option.id">{{ option.content }}</label> <label :for="option.id">{{ option.content }}</label>
</li> </li>
</ul> </ul>
</div> </div>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { Component, Vue, Prop } from "vue-property-decorator"; import { Component, Vue, Prop } from "vue-property-decorator";
@Component @Component
export default class SelectBox extends Vue { export default class SelectBox extends Vue {
@Prop() title!: string; @Prop() title!: string;
@Prop() optionList!: string[]; @Prop() optionList!: string[];
selectedOption: string = ""; selectedOption: string = "";
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.option { .option {
&-container { &-container {
position: relative; position: relative;
input { input {
display: none; display: none;
} }
label { label {
padding: 0.5rem 1rem; padding: 0.5rem 1rem;
width: 100%; width: 100%;
cursor: pointer; cursor: pointer;
} }
} }
&-item { &-item {
display: flex; display: flex;
&:hover { &:hover {
background-color: rgba(#868686, 0.85); background-color: rgba(#868686, 0.85);
} }
transition: background 150ms ease-in; transition: background 150ms ease-in;
} }
&-selected, &-selected,
&-list { &-list {
background: #333; background: #333;
border-radius: 0.5em; border-radius: 0.5em;
} }
&-selected { &-selected {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding: 0.5rem 1rem; padding: 0.5rem 1rem;
min-width: 150px; min-width: 150px;
cursor: pointer; cursor: pointer;
span { span {
margin-right: 2rem; margin-right: 2rem;
} }
img { img {
max-width: 0.75em; max-width: 0.75em;
} }
} }
&-list { &-list {
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
z-index: 10; z-index: 10;
width: 100%; width: 100%;
background-color: rgba(#222, 0.95); background-color: rgba(#222, 0.95);
overflow: hidden; overflow: hidden;
max-height: 0; max-height: 0;
&.open { &.open {
max-height: 250px; max-height: 250px;
opacity: 1; opacity: 1;
} }
transition: all 150ms ease-in; transition: all 150ms ease-in;
} }
} }
</style> </style>
+403 -403
View File
@@ -1,404 +1,404 @@
<template> <template>
<div class="scenery-info"> <div class="scenery-info">
<div class="info-header"> <div class="info-header">
<div class="scenery-name"> <div class="scenery-name">
<a <a
v-if="stationInfo.stationURL" v-if="stationInfo.stationURL"
:href="stationInfo.stationURL" :href="stationInfo.stationURL"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
>{{ stationInfo.stationName }}</a> >{{ stationInfo.stationName }}</a>
<span v-else>{{ stationInfo.stationName }}</span> <span v-else>{{ stationInfo.stationName }}</span>
</div> </div>
<div class="scenery-hash">#{{ stationInfo.stationHash }}</div> <div class="scenery-hash">#{{ stationInfo.stationHash }}</div>
</div> </div>
<section v-if="!timetableOnly"> <section v-if="!timetableOnly">
<div class="info-stats"> <div class="info-stats">
<span class="likes"> <span class="likes">
<img :src="likeIcon" alt="icon-like" /> <img :src="likeIcon" alt="icon-like" />
<span>{{ stationInfo.dispatcherRate }}</span> <span>{{ stationInfo.dispatcherRate }}</span>
</span> </span>
<span class="users"> <span class="users">
<img :src="userIcon" alt="icon-user" /> <img :src="userIcon" alt="icon-user" />
<span>{{ stationInfo.currentUsers }}</span> <span>{{ stationInfo.currentUsers }}</span>
/ /
<span>{{ stationInfo.maxUsers }}</span> <span>{{ stationInfo.maxUsers }}</span>
</span> </span>
<span class="spawns"> <span class="spawns">
<img :src="spawnIcon" alt="icon-spawn" /> <img :src="spawnIcon" alt="icon-spawn" />
<span>{{ stationInfo.spawns.length }}</span> <span>{{ stationInfo.spawns.length }}</span>
</span> </span>
<span class="schedules"> <span class="schedules">
<img :src="timetableIcon" alt="icon-timetable" /> <img :src="timetableIcon" alt="icon-timetable" />
<span v-if="stationInfo.scheduledTrains"> <span v-if="stationInfo.scheduledTrains">
<span style="color: #eee">{{stationInfo.scheduledTrains.length}}</span> <span style="color: #eee">{{stationInfo.scheduledTrains.length}}</span>
/ /
<span <span
style="color: #bbb" style="color: #bbb"
>{{ stationInfo.scheduledTrains.filter(train => train.stopInfo.confirmed).length }}</span> >{{ stationInfo.scheduledTrains.filter(train => train.stopInfo.confirmed).length }}</span>
</span> </span>
</span> </span>
</div> </div>
<div class="info-brief"> <div class="info-brief">
<img <img
v-if="stationInfo.controlType" v-if="stationInfo.controlType"
:src="require(`@/assets/icon-${stationInfo.controlType}.svg`)" :src="require(`@/assets/icon-${stationInfo.controlType}.svg`)"
:alt="stationInfo.controlType" :alt="stationInfo.controlType"
:title="'Sterowanie ' + stationInfo.controlType" :title="'Sterowanie ' + stationInfo.controlType"
/> />
<img <img
v-if="stationInfo.signalType" v-if="stationInfo.signalType"
:src="require(`@/assets/icon-${stationInfo.signalType}.svg`)" :src="require(`@/assets/icon-${stationInfo.signalType}.svg`)"
:alt="stationInfo.signalType" :alt="stationInfo.signalType"
:title="'Sygnalizacja ' + stationInfo.signalType" :title="'Sygnalizacja ' + stationInfo.signalType"
/> />
<img <img
v-if="stationInfo.SBL && stationInfo.SBL !== ''" v-if="stationInfo.SBL && stationInfo.SBL !== ''"
:src="require(`@/assets/icon-SBL.svg`)" :src="require(`@/assets/icon-SBL.svg`)"
alt="SBL" alt="SBL"
title="Sceneria posiada SBL na przynajmniej jednym ze szlaków" title="Sceneria posiada SBL na przynajmniej jednym ze szlaków"
/> />
<img <img
v-if="stationInfo.default" v-if="stationInfo.default"
:src="require(`@/assets/icon-td2.svg`)" :src="require(`@/assets/icon-td2.svg`)"
alt="default-pack" alt="default-pack"
title="Sceneria domyślnie dostępna w grze" title="Sceneria domyślnie dostępna w grze"
/> />
<img <img
v-if="stationInfo.nonPublic || !stationInfo.reqLevel" v-if="stationInfo.nonPublic || !stationInfo.reqLevel"
:src="require(`@/assets/icon-lock.svg`)" :src="require(`@/assets/icon-lock.svg`)"
alt="non-public" alt="non-public"
title="Sceneria niepubliczna" title="Sceneria niepubliczna"
/> />
<img <img
v-if="stationInfo.unavailable" v-if="stationInfo.unavailable"
:src="require(`@/assets/icon-unavailable.svg`)" :src="require(`@/assets/icon-unavailable.svg`)"
alt="icon-unavailable" alt="icon-unavailable"
title="Sceneria niedostępna" title="Sceneria niedostępna"
/> />
</div> </div>
<div class="info-dispatcher"> <div class="info-dispatcher">
<div> <div>
<span <span
class="level" class="level"
:style="calculateExpStyle(stationInfo.dispatcherExp, stationInfo.dispatcherIsSupporter)" :style="calculateExpStyle(stationInfo.dispatcherExp, stationInfo.dispatcherIsSupporter)"
>{{ stationInfo.dispatcherExp > 1 ? stationInfo.dispatcherExp : "L"}}</span> >{{ stationInfo.dispatcherExp > 1 ? stationInfo.dispatcherExp : "L"}}</span>
<span class="name">{{ stationInfo.dispatcherName }}</span> <span class="name">{{ stationInfo.dispatcherName }}</span>
</div> </div>
<span <span
class="status" class="status"
:class="statusClasses(stationInfo.occupiedTo)" :class="statusClasses(stationInfo.occupiedTo)"
>{{ stationInfo.occupiedTo }}</span> >{{ stationInfo.occupiedTo }}</span>
</div> </div>
<div class="info-lists"> <div class="info-lists">
<div class="user-list"> <div class="user-list">
<h3 class="user-header"> <h3 class="user-header">
GRACZE ONLINE GRACZE ONLINE
<img :src="userIcon" alt="icon-user" /> <img :src="userIcon" alt="icon-user" />
</h3> </h3>
<div <div
v-for="(train, i) in computedStationTrains" v-for="(train, i) in computedStationTrains"
class="user" class="user"
:class="train.stopStatus" :class="train.stopStatus"
:key="train.trainNo + i" :key="train.trainNo + i"
@click="() => navigateToTrain(train.trainNo)" @click="() => navigateToTrain(train.trainNo)"
> >
<span class="user_train">{{ train.trainNo }}</span> <span class="user_train">{{ train.trainNo }}</span>
<span class="user_name">{{ train.driverName }}</span> <span class="user_name">{{ train.driverName }}</span>
</div> </div>
<div <div
class="user offline" class="user offline"
v-if="!computedStationTrains || computedStationTrains.length == 0" v-if="!computedStationTrains || computedStationTrains.length == 0"
>BRAK AKTYWNYCH GRACZY</div> >BRAK AKTYWNYCH GRACZY</div>
</div> </div>
<div class="spawn-list"> <div class="spawn-list">
<h3 class="spawn-header"> <h3 class="spawn-header">
OTWARTE SPAWNY OTWARTE SPAWNY
<img :src="spawnIcon" alt="icon-spawn" /> <img :src="spawnIcon" alt="icon-spawn" />
</h3> </h3>
<span <span
class="spawn" class="spawn"
v-for="(spawn, i) in stationInfo.spawns" v-for="(spawn, i) in stationInfo.spawns"
:key="spawn.spawnName + stationInfo.dispatcherName + i" :key="spawn.spawnName + stationInfo.dispatcherName + i"
> >
<span class="spawn_name">{{ spawn.spawnName }}</span> <span class="spawn_name">{{ spawn.spawnName }}</span>
<span class="spawn_length">{{ spawn.spawnLength }}m</span> <span class="spawn_length">{{ spawn.spawnLength }}m</span>
</span> </span>
<span <span
class="spawn none" class="spawn none"
v-if="!stationInfo.spawns || stationInfo.spawns.length == 0" v-if="!stationInfo.spawns || stationInfo.spawns.length == 0"
>BRAK OTWARTYCH SPAWNÓW</span> >BRAK OTWARTYCH SPAWNÓW</span>
</div> </div>
</div> </div>
</section> </section>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { Component, Vue, Prop } from "vue-property-decorator"; import { Component, Vue, Prop } from "vue-property-decorator";
import Station from "@/scripts/interfaces/Station"; import Station from "@/scripts/interfaces/Station";
import Train from "@/scripts/interfaces/Train"; import Train from "@/scripts/interfaces/Train";
import styleMixin from "@/mixins/styleMixin"; import styleMixin from "@/mixins/styleMixin";
@Component @Component
export default class SceneryInfo extends styleMixin { export default class SceneryInfo extends styleMixin {
@Prop() readonly stationInfo!: Station; @Prop() readonly stationInfo!: Station;
@Prop() readonly timetableOnly!: boolean; @Prop() readonly timetableOnly!: boolean;
likeIcon: string = require("@/assets/icon-like.svg"); likeIcon: string = require("@/assets/icon-like.svg");
spawnIcon: string = require("@/assets/icon-spawn.svg"); spawnIcon: string = require("@/assets/icon-spawn.svg");
timetableIcon: string = require("@/assets/icon-timetable.svg"); timetableIcon: string = require("@/assets/icon-timetable.svg");
userIcon: string = require("@/assets/icon-user.svg"); userIcon: string = require("@/assets/icon-user.svg");
get computedStationTrains() { get computedStationTrains() {
if (!this.stationInfo) return null; if (!this.stationInfo) return null;
return this.stationInfo.stationTrains.map((stationTrain) => { return this.stationInfo.stationTrains.map((stationTrain) => {
const scheduledData = this.stationInfo?.scheduledTrains.find( const scheduledData = this.stationInfo?.scheduledTrains.find(
(scheduledTrain) => scheduledTrain.trainNo === stationTrain.trainNo (scheduledTrain) => scheduledTrain.trainNo === stationTrain.trainNo
); );
return { return {
...stationTrain, ...stationTrain,
stopStatus: scheduledData?.stopStatus || "no-timetable", stopStatus: scheduledData?.stopStatus || "no-timetable",
}; };
}); });
} }
navigateToTrain(trainNo: number) { navigateToTrain(trainNo: number) {
this.$router.push({ this.$router.push({
name: "TrainsView", name: "TrainsView",
params: { passedSearchedTrain: trainNo.toString() }, params: { passedSearchedTrain: trainNo.toString() },
}); });
} }
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import "../../styles/responsive.scss"; @import "../../styles/responsive.scss";
@import "../../styles/user_badge.scss"; @import "../../styles/user_badge.scss";
@import "../../styles/variables.scss"; @import "../../styles/variables.scss";
h3 { h3 {
margin: 0.5em 0; margin: 0.5em 0;
padding: 0.3em; padding: 0.3em;
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
font-size: 1.2em; font-size: 1.2em;
img { img {
width: 1.1em; width: 1.1em;
margin-left: 0.5em; margin-left: 0.5em;
} }
} }
.info { .info {
&-header { &-header {
padding: 1rem; padding: 1rem;
& > .scenery-name { & > .scenery-name {
font-size: 3em; font-size: 3em;
font-weight: bold; font-weight: bold;
color: $accentCol; color: $accentCol;
text-transform: uppercase; text-transform: uppercase;
} }
& > .scenery-hash { & > .scenery-hash {
font-size: 1em; font-size: 1em;
line-height: 0.8em; line-height: 0.8em;
color: #aaa; color: #aaa;
} }
} }
&-stats { &-stats {
font-size: 1.3em; font-size: 1.3em;
padding: 1rem 0; padding: 1rem 0;
display: flex; display: flex;
justify-content: center; justify-content: center;
& > span { & > span {
display: flex; display: flex;
align-items: center; align-items: center;
margin: 0 0.6em; margin: 0 0.6em;
} }
.likes, .likes,
.spawns { .spawns {
color: $accentCol; color: $accentCol;
} }
span > img { span > img {
width: 1.2em; width: 1.2em;
margin-right: 0.5em; margin-right: 0.5em;
} }
} }
&-brief { &-brief {
padding: 1rem 0; padding: 1rem 0;
img { img {
width: 2.5em; width: 2.5em;
margin: 0 0.5rem; margin: 0 0.5rem;
} }
} }
&-dispatcher { &-dispatcher {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
.level { .level {
display: inline-block; display: inline-block;
margin-right: 0.3em; margin-right: 0.3em;
background: firebrick; background: firebrick;
border-radius: 0.1em; border-radius: 0.1em;
width: 1.5em; width: 1.5em;
height: 1.5em; height: 1.5em;
line-height: 1.5em; line-height: 1.5em;
font-size: 2em; font-size: 2em;
font-weight: bold; font-weight: bold;
} }
.name { .name {
font-size: 1.6em; font-size: 1.6em;
margin-right: 1em; margin-right: 1em;
} }
.status { .status {
font-size: 1em; font-size: 1em;
border-radius: 1em; border-radius: 1em;
} }
} }
&-lists { &-lists {
display: flex; display: flex;
align-items: center; align-items: center;
flex-direction: column; flex-direction: column;
& > .user-list { & > .user-list {
ul { ul {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
justify-content: center; justify-content: center;
} }
} }
} }
} }
.user, .user,
.spawn { .spawn {
font-weight: 600; font-weight: 600;
font-size: 0.9em; font-size: 0.9em;
display: inline-block; display: inline-block;
padding: 0; padding: 0;
background: #585858; background: #585858;
margin: 0.25em; margin: 0.25em;
span { span {
display: inline-block; display: inline-block;
padding: 0.2em 0.4em; padding: 0.2em 0.4em;
} }
@include smallScreen() { @include smallScreen() {
font-size: 1em; font-size: 1em;
} }
} }
.user { .user {
cursor: pointer; cursor: pointer;
&_train { &_train {
color: black; color: black;
background-color: $no-timetable; background-color: $no-timetable;
transition: background-color 200ms; transition: background-color 200ms;
-ms-transition: background-color 200ms; -ms-transition: background-color 200ms;
-webkit-transition: background-color 200ms; -webkit-transition: background-color 200ms;
} }
&.no-timetable { &.no-timetable {
pointer-events: none; pointer-events: none;
& > .user_train { & > .user_train {
background-color: $no-timetable; background-color: $no-timetable;
} }
} }
&.departed > &_train { &.departed > &_train {
background-color: $departed; background-color: $departed;
} }
&.stopped > &_train { &.stopped > &_train {
background-color: $stopped; background-color: $stopped;
} }
&.online > &_train { &.online > &_train {
background-color: $online; background-color: $online;
} }
&.terminated > &_train { &.terminated > &_train {
background-color: $terminated; background-color: $terminated;
} }
&.disconnected > &_train { &.disconnected > &_train {
background-color: $disconnected; background-color: $disconnected;
} }
&.offline { &.offline {
background: firebrick; background: firebrick;
pointer-events: none; pointer-events: none;
} }
} }
.spawn { .spawn {
&_length { &_length {
background: $accentCol; background: $accentCol;
color: black; color: black;
} }
} }
.spawn.none, .spawn.none,
.user.offline { .user.offline {
font-weight: 600; font-weight: 600;
padding: 0.2em 0.4em; padding: 0.2em 0.4em;
background: firebrick; background: firebrick;
text-align: center; text-align: center;
@include smallScreen() { @include smallScreen() {
font-size: 1em; font-size: 1em;
} }
} }
</style> </style>
+466 -466
View File
@@ -1,467 +1,467 @@
<template> <template>
<div class="scenery-timetable"> <div class="scenery-timetable">
<h3 class="timetable-header"> <h3 class="timetable-header">
<span>AKTYWNE ROZKŁADY JAZDY</span> <span>AKTYWNE ROZKŁADY JAZDY</span>
<a v-if="!timetableOnly" :href="currentURL + '&timetable_only=1'" target="_blank"> <a v-if="!timetableOnly" :href="currentURL + '&timetable_only=1'" target="_blank">
<img :src="viewIcon" alt="icon-view" title="Wyodrębnij rozkłady jazdy" /> <img :src="viewIcon" alt="icon-view" title="Wyodrębnij rozkłady jazdy" />
</a> </a>
</h3> </h3>
<div class="select-box" v-if="stationInfo.checkpoints"> <div class="select-box" v-if="stationInfo.checkpoints">
<div class="option-container"> <div class="option-container">
<div class="option-selected" @click="toggleOptionList"> <div class="option-selected" @click="toggleOptionList">
<span>{{ selectedOption }}</span> <span>{{ selectedOption }}</span>
<img :src="require('@/assets/icon-select.svg')" alt="icon-select" /> <img :src="require('@/assets/icon-select.svg')" alt="icon-select" />
</div> </div>
<ul class="option-list" :class="{ open: listOpen }"> <ul class="option-list" :class="{ open: listOpen }">
<li <li
class="option-item" class="option-item"
v-for="(cp, i) in stationInfo.checkpoints" v-for="(cp, i) in stationInfo.checkpoints"
:key="i" :key="i"
@click="() => chooseOption(cp.checkpointName)" @click="() => chooseOption(cp.checkpointName)"
> >
<input type="option-radio" name="sort" /> <input type="option-radio" name="sort" />
<label :id="cp.checkpointName">{{ cp.checkpointName }}</label> <label :id="cp.checkpointName">{{ cp.checkpointName }}</label>
</li> </li>
</ul> </ul>
</div> </div>
</div> </div>
<span class="timetable-item loading" v-if="dataStatus == 0">Ładowanie...</span> <span class="timetable-item loading" v-if="dataStatus == 0">Ładowanie...</span>
<span <span
class="timetable-item empty" class="timetable-item empty"
v-else-if="computedScheduledTrains.length == 0" v-else-if="computedScheduledTrains.length == 0"
>Brak aktywnych rozkładów!</span> >Brak aktywnych rozkładów!</span>
<transition-group name="list-anim"> <transition-group name="list-anim">
<div class="timetable-item" v-for="(scheduledTrain, i) in computedScheduledTrains" :key="i+1"> <div class="timetable-item" v-for="(scheduledTrain, i) in computedScheduledTrains" :key="i+1">
<span class="timetable-general"> <span class="timetable-general">
<span class="general-info"> <span class="general-info">
<router-link <router-link
:to="{ :to="{
name: 'TrainsView', name: 'TrainsView',
params: { params: {
passedSearchedTrain: scheduledTrain.trainNo.toString(), passedSearchedTrain: scheduledTrain.trainNo.toString(),
}, },
}" }"
> >
<span> <span>
<strong>{{ scheduledTrain.category }}</strong> <strong>{{ scheduledTrain.category }}</strong>
{{ scheduledTrain.trainNo }} {{ scheduledTrain.trainNo }}
</span> </span>
</router-link>| </router-link>|
<span> <span>
<a <a
:href=" :href="
'https://td2.info.pl/profile/?u=' + scheduledTrain.driverId 'https://td2.info.pl/profile/?u=' + scheduledTrain.driverId
" "
target="_blank" target="_blank"
>{{ scheduledTrain.driverName }}</a> >{{ scheduledTrain.driverName }}</a>
</span> </span>
<div class="info-route"> <div class="info-route">
<strong>{{ scheduledTrain.beginsAt }} - {{ scheduledTrain.terminatesAt }}</strong> <strong>{{ scheduledTrain.beginsAt }} - {{ scheduledTrain.terminatesAt }}</strong>
</div> </div>
</span> </span>
<span class="general-status"> <span class="general-status">
<span :class="scheduledTrain.stopStatus">{{scheduledTrain.stopLabel}}</span> <span :class="scheduledTrain.stopStatus">{{scheduledTrain.stopLabel}}</span>
</span> </span>
</span> </span>
<span class="timetable-schedule"> <span class="timetable-schedule">
<span class="schedule-arrival"> <span class="schedule-arrival">
<span class="arrival-time begins" v-if="scheduledTrain.stopInfo.beginsHere"> <span class="arrival-time begins" v-if="scheduledTrain.stopInfo.beginsHere">
ROZPOCZYNA ROZPOCZYNA
<div>BIEG</div> <div>BIEG</div>
</span> </span>
<span class="arrival-time" v-else> <span class="arrival-time" v-else>
{{ scheduledTrain.stopInfo.arrivalTimeString }} ({{ {{ scheduledTrain.stopInfo.arrivalTimeString }} ({{
scheduledTrain.stopInfo.arrivalDelay scheduledTrain.stopInfo.arrivalDelay
}}) }})
</span> </span>
</span> </span>
<span class="schedule-stop"> <span class="schedule-stop">
<span class="stop-time" v-if="scheduledTrain.stopInfo.stopTime"> <span class="stop-time" v-if="scheduledTrain.stopInfo.stopTime">
{{ scheduledTrain.stopInfo.stopTime }} {{ scheduledTrain.stopInfo.stopTime }}
{{ scheduledTrain.stopInfo.stopType }} {{ scheduledTrain.stopInfo.stopType }}
</span> </span>
<span class="stop-arrow arrow"></span> <span class="stop-arrow arrow"></span>
</span> </span>
<span class="schedule-departure"> <span class="schedule-departure">
<span <span
class="departure-time terminates" class="departure-time terminates"
v-if="scheduledTrain.stopInfo.terminatesHere" v-if="scheduledTrain.stopInfo.terminatesHere"
>KOŃCZY BIEG</span> >KOŃCZY BIEG</span>
<span class="departure-time" v-else> <span class="departure-time" v-else>
{{ scheduledTrain.stopInfo.departureTimeString }} ({{ {{ scheduledTrain.stopInfo.departureTimeString }} ({{
scheduledTrain.stopInfo.departureDelay scheduledTrain.stopInfo.departureDelay
}}) }})
</span> </span>
</span> </span>
</span> </span>
</div> </div>
</transition-group> </transition-group>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { Component, Vue, Prop } from "vue-property-decorator"; import { Component, Vue, Prop } from "vue-property-decorator";
import Station from "@/scripts/interfaces/Station"; import Station from "@/scripts/interfaces/Station";
import ScheduledTrain from "@/scripts/interfaces/ScheduledTrain"; import ScheduledTrain from "@/scripts/interfaces/ScheduledTrain";
@Component @Component
export default class SceneryTimetable extends Vue { export default class SceneryTimetable extends Vue {
@Prop() readonly stationInfo!: Station; @Prop() readonly stationInfo!: Station;
@Prop() readonly timetableOnly!: boolean; @Prop() readonly timetableOnly!: boolean;
@Prop() readonly dataStatus!: number; @Prop() readonly dataStatus!: number;
viewIcon: string = require("@/assets/icon-view.svg"); viewIcon: string = require("@/assets/icon-view.svg");
listOpen: boolean = false; listOpen: boolean = false;
selectedOption: string = ""; selectedOption: string = "";
mounted() { mounted() {
if (!this.stationInfo.checkpoints) return; if (!this.stationInfo.checkpoints) return;
if (this.selectedOption == "") if (this.selectedOption == "")
this.selectedOption = this.stationInfo.checkpoints[0].checkpointName; this.selectedOption = this.stationInfo.checkpoints[0].checkpointName;
} }
activated() { activated() {
if (!this.stationInfo) return; if (!this.stationInfo) return;
if (!this.stationInfo.checkpoints) return; if (!this.stationInfo.checkpoints) return;
if (this.selectedOption == "") if (this.selectedOption == "")
this.selectedOption = this.stationInfo.checkpoints[0].checkpointName; this.selectedOption = this.stationInfo.checkpoints[0].checkpointName;
} }
toggleOptionList() { toggleOptionList() {
this.listOpen = !this.listOpen; this.listOpen = !this.listOpen;
} }
closeOptionList() { closeOptionList() {
this.listOpen = false; this.listOpen = false;
} }
chooseOption(name: string) { chooseOption(name: string) {
this.selectedOption = name; this.selectedOption = name;
this.closeOptionList(); this.closeOptionList();
} }
get currentURL() { get currentURL() {
return `${location.origin}/scenery?hash=${this.stationInfo?.stationHash}`; return `${location.origin}/scenery?hash=${this.stationInfo?.stationHash}`;
} }
get computedScheduledTrains() { get computedScheduledTrains() {
if (!this.stationInfo) return []; if (!this.stationInfo) return [];
let scheduledTrains: ScheduledTrain[] | undefined; let scheduledTrains: ScheduledTrain[] | undefined;
if (this.stationInfo.checkpoints) if (this.stationInfo.checkpoints)
scheduledTrains = this.stationInfo.checkpoints.find( scheduledTrains = this.stationInfo.checkpoints.find(
(cp) => cp.checkpointName === this.selectedOption (cp) => cp.checkpointName === this.selectedOption
)?.scheduledTrains; )?.scheduledTrains;
else scheduledTrains = this.stationInfo.scheduledTrains; else scheduledTrains = this.stationInfo.scheduledTrains;
return ( return (
scheduledTrains?.sort((a, b) => { scheduledTrains?.sort((a, b) => {
if (a.stopStatusID > b.stopStatusID) return 1; if (a.stopStatusID > b.stopStatusID) return 1;
else if (a.stopStatusID < b.stopStatusID) return -1; else if (a.stopStatusID < b.stopStatusID) return -1;
if (a.stopInfo.arrivalTimestamp > b.stopInfo.arrivalTimestamp) return 1; if (a.stopInfo.arrivalTimestamp > b.stopInfo.arrivalTimestamp) return 1;
else if (a.stopInfo.arrivalTimestamp < b.stopInfo.arrivalTimestamp) else if (a.stopInfo.arrivalTimestamp < b.stopInfo.arrivalTimestamp)
return -1; return -1;
return a.stopInfo.departureTimestamp > b.stopInfo.departureTimestamp return a.stopInfo.departureTimestamp > b.stopInfo.departureTimestamp
? 1 ? 1
: -1; : -1;
}) || [] }) || []
); );
} }
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import "../../styles/responsive.scss"; @import "../../styles/responsive.scss";
@import "../../styles/variables.scss"; @import "../../styles/variables.scss";
h3 { h3 {
margin: 0.5em 0; margin: 0.5em 0;
padding: 0.3em; padding: 0.3em;
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
font-size: 1.2em; font-size: 1.2em;
img { img {
width: 1.1em; width: 1.1em;
margin-left: 0.5em; margin-left: 0.5em;
} }
} }
.list-anim { .list-anim {
&-enter-active, &-enter-active,
&-leave-active { &-leave-active {
transition: all 250ms ease-out; transition: all 250ms ease-out;
} }
&-enter, &-enter,
&-leave-to { &-leave-to {
opacity: 0; opacity: 0;
transform: scale(0.9); transform: scale(0.9);
} }
&-move { &-move {
transition: transform 100ms; transition: transform 100ms;
} }
} }
.select-box { .select-box {
display: flex; display: flex;
justify-content: center; justify-content: center;
} }
.option { .option {
&-container { &-container {
position: relative; position: relative;
input { input {
display: none; display: none;
} }
label { label {
padding: 0.5rem 1rem; padding: 0.5rem 1rem;
cursor: pointer; cursor: pointer;
} }
} }
&-item { &-item {
display: flex; display: flex;
justify-content: center; justify-content: center;
&:hover { &:hover {
background-color: rgba(#868686, 0.85); background-color: rgba(#868686, 0.85);
} }
transition: background 150ms ease-in; transition: background 150ms ease-in;
} }
&-selected, &-selected,
&-list { &-list {
background: #444; background: #444;
border-radius: 0.5em; border-radius: 0.5em;
} }
&-selected { &-selected {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding: 0.5rem 1rem; padding: 0.5rem 1rem;
min-width: 10em; min-width: 10em;
cursor: pointer; cursor: pointer;
span { span {
margin-right: 2rem; margin-right: 2rem;
} }
img { img {
max-width: 0.75em; max-width: 0.75em;
} }
} }
&-list { &-list {
position: absolute; position: absolute;
top: 100%; top: 100%;
left: 0; left: 0;
width: 100%; width: 100%;
z-index: 10; z-index: 10;
background-color: rgba(#222, 0.95); background-color: rgba(#222, 0.95);
overflow: hidden; overflow: hidden;
max-height: 0; max-height: 0;
&.open { &.open {
max-height: 250px; max-height: 250px;
opacity: 1; opacity: 1;
} }
transition: all 150ms ease-in; transition: all 150ms ease-in;
} }
} }
.timetable { .timetable {
&-header { &-header {
a { a {
display: flex; display: flex;
} }
img { img {
cursor: pointer; cursor: pointer;
} }
} }
&-item { &-item {
margin: 1em auto; margin: 1em auto;
font-size: 0.8em; font-size: 0.8em;
display: grid; display: grid;
grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); grid-template-columns: repeat(auto-fit, minmax(0, 1fr));
padding: 0 0.5rem; padding: 0 0.5rem;
background: #555; background: #555;
@include smallScreen() { @include smallScreen() {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
} }
&.loading, &.loading,
&.empty { &.empty {
padding: 1rem; padding: 1rem;
font-size: 1em; font-size: 1em;
} }
&.empty { &.empty {
color: $accentCol; color: $accentCol;
} }
} }
&-general { &-general {
padding: 0.5rem 0.3rem; padding: 0.5rem 0.3rem;
border-radius: 10px; border-radius: 10px;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
text-align: left; text-align: left;
overflow: hidden; overflow: hidden;
@include smallScreen() { @include smallScreen() {
width: 95%; width: 95%;
font-size: 1.3em; font-size: 1.3em;
} }
} }
&-schedule { &-schedule {
display: grid; display: grid;
grid-template-columns: repeat(auto-fit, minmax(50px, 1fr)); grid-template-columns: repeat(auto-fit, minmax(50px, 1fr));
font-size: 1.2em; font-size: 1.2em;
@include smallScreen() { @include smallScreen() {
width: 100%; width: 100%;
margin: 0.7em 0; margin: 0.7em 0;
font-size: 1.8em; font-size: 1.8em;
} }
} }
} }
.arrow { .arrow {
border: solid white; border: solid white;
border-width: 0 2px 2px 0; border-width: 0 2px 2px 0;
display: inline-block; display: inline-block;
padding: 2px; padding: 2px;
margin-left: 50px; margin-left: 50px;
position: relative; position: relative;
transform: rotate(-45deg); transform: rotate(-45deg);
&::before { &::before {
content: ""; content: "";
position: absolute; position: absolute;
display: block; display: block;
width: 55px; width: 55px;
height: 3px; height: 3px;
top: 4px; top: 4px;
left: 4px; left: 4px;
transform: translate(-100%, -1px) rotate(45deg); transform: translate(-100%, -1px) rotate(45deg);
transform-origin: right bottom; transform-origin: right bottom;
background: white; background: white;
} }
} }
.general-info { .general-info {
span { span {
color: $accentCol; color: $accentCol;
} }
.info-route { .info-route {
margin-top: 0.5em; margin-top: 0.5em;
} }
} }
.general-status { .general-status {
span.arriving { span.arriving {
color: #aaa; color: #aaa;
} }
span.departed { span.departed {
color: lime; color: lime;
font-weight: bold; font-weight: bold;
&-away { &-away {
font-weight: bold; font-weight: bold;
color: rgb(0, 155, 0); color: rgb(0, 155, 0);
} }
} }
span.stopped { span.stopped {
color: #ffa600; color: #ffa600;
font-weight: bold; font-weight: bold;
} }
span.online { span.online {
color: gold; color: gold;
} }
span.terminated { span.terminated {
color: #e00000; color: #e00000;
font-weight: bold; font-weight: bold;
} }
} }
.schedule { .schedule {
&-arrival, &-arrival,
&-stop, &-stop,
&-departure { &-departure {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
margin: 0 0.3rem; margin: 0 0.3rem;
} }
&-stop { &-stop {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
.stop-time { .stop-time {
font-size: 0.7em; font-size: 0.7em;
margin: 5px 0; margin: 5px 0;
} }
} }
} }
.arrival-time.begins, .arrival-time.begins,
.departure-time.terminates { .departure-time.terminates {
font-size: 0.75em; font-size: 0.75em;
} }
</style> </style>
+421 -421
View File
@@ -1,422 +1,422 @@
<template> <template>
<section class="filter-card"> <section class="filter-card">
<div class="card-exit" @click="exit"> <div class="card-exit" @click="exit">
<img :src="require('@/assets/icon-exit.svg')" alt="exit icon" /> <img :src="require('@/assets/icon-exit.svg')" alt="exit icon" />
</div> </div>
<div class="card-title flex">FILTRUJ STACJE</div> <div class="card-title flex">FILTRUJ STACJE</div>
<div class="card-options"> <div class="card-options">
<div class="option" v-for="(option, i) in inputs.options" :key="i"> <div class="option" v-for="(option, i) in inputs.options" :key="i">
<label class="option-label"> <label class="option-label">
<input <input
class="option-input" class="option-input"
type="checkbox" type="checkbox"
:name="option.name" :name="option.name"
:defaultValue="option.defaultValue" :defaultValue="option.defaultValue"
:id="option.id" :id="option.id"
v-model="option.value" v-model="option.value"
@change="handleChange" @change="handleChange"
/> />
<span <span
class="option-content" class="option-content"
:class="option.section + (option.value ? ' checked' : '')" :class="option.section + (option.value ? ' checked' : '')"
>{{ option.content }}</span> >{{ option.content }}</span>
</label> </label>
</div> </div>
</div> </div>
<div class="card-sliders"> <div class="card-sliders">
<div class="slider" v-for="(slider, i) in inputs.sliders" :key="i"> <div class="slider" v-for="(slider, i) in inputs.sliders" :key="i">
<input <input
class="slider-input" class="slider-input"
type="range" type="range"
:name="slider.name" :name="slider.name"
:id="slider.id" :id="slider.id"
:min="slider.minRange" :min="slider.minRange"
:max="slider.maxRange" :max="slider.maxRange"
v-model="slider.value" v-model="slider.value"
@change="handleInput" @change="handleInput"
/> />
<span class="slider-value">{{ slider.value }}</span> <span class="slider-value">{{ slider.value }}</span>
<div class="slider-content">{{ slider.content }}</div> <div class="slider-content">{{ slider.content }}</div>
</div> </div>
</div> </div>
<div class="card-save"> <div class="card-save">
<div class="option save"> <div class="option save">
<label class="option-label"> <label class="option-label">
<input class="option-input" type="checkbox" v-model="saveOptions" @change="saveFilters" /> <input class="option-input" type="checkbox" v-model="saveOptions" @change="saveFilters" />
<span class="option-content save" :class="{ checked: saveOptions }">ZAPISZ FILTRY</span> <span class="option-content save" :class="{ checked: saveOptions }">ZAPISZ FILTRY</span>
</label> </label>
</div> </div>
</div> </div>
<div class="card-actions flex"> <div class="card-actions flex">
<button class="button" @click="resetFilters">RESET FILTRÓW</button> <button class="button" @click="resetFilters">RESET FILTRÓW</button>
<button class="button" @click="exit">ZAMKNIJ FILTRY</button> <button class="button" @click="exit">ZAMKNIJ FILTRY</button>
</div> </div>
</section> </section>
</template> </template>
<script lang="ts"> <script lang="ts">
import { Vue, Component, Prop } from "vue-property-decorator"; import { Vue, Component, Prop } from "vue-property-decorator";
import inputData from "@/data/options.json"; import inputData from "@/data/options.json";
import StorageManager from "@/scripts/storageManager"; import StorageManager from "@/scripts/storageManager";
@Component @Component
export default class FilterCard extends Vue { export default class FilterCard extends Vue {
inputs = { ...inputData }; inputs = { ...inputData };
saveOptions: boolean = false; saveOptions: boolean = false;
STORAGE_KEY: string = "options_saved"; STORAGE_KEY: string = "options_saved";
@Prop() exit!: () => void; @Prop() exit!: () => void;
mounted() { mounted() {
this.saveOptions = StorageManager.isRegistered(this.STORAGE_KEY); this.saveOptions = StorageManager.isRegistered(this.STORAGE_KEY);
} }
handleChange(e: Event): void { handleChange(e: Event): void {
const target = <HTMLInputElement>e.target; const target = <HTMLInputElement>e.target;
this.$emit("changeFilterValue", { this.$emit("changeFilterValue", {
name: target.name, name: target.name,
value: !target.checked, value: !target.checked,
}); });
if (this.saveOptions) if (this.saveOptions)
StorageManager.setBooleanValue(target.name, target.checked); StorageManager.setBooleanValue(target.name, target.checked);
} }
handleInput(e: Event): void { handleInput(e: Event): void {
const target = <HTMLInputElement>e.target; const target = <HTMLInputElement>e.target;
this.$emit("changeFilterValue", { this.$emit("changeFilterValue", {
name: target.name, name: target.name,
value: target.value, value: target.value,
}); });
if (this.saveOptions) if (this.saveOptions)
StorageManager.setStringValue(target.name, target.value); StorageManager.setStringValue(target.name, target.value);
} }
saveFilters(): void { saveFilters(): void {
if (!this.saveOptions) { if (!this.saveOptions) {
StorageManager.unregisterStorage(this.STORAGE_KEY); StorageManager.unregisterStorage(this.STORAGE_KEY);
console.log(this.saveOptions); console.log(this.saveOptions);
return; return;
} }
StorageManager.registerStorage(this.STORAGE_KEY); StorageManager.registerStorage(this.STORAGE_KEY);
this.inputs.options.forEach((option) => this.inputs.options.forEach((option) =>
StorageManager.setBooleanValue(option.name, option.value) StorageManager.setBooleanValue(option.name, option.value)
); );
this.inputs.sliders.forEach((slider) => this.inputs.sliders.forEach((slider) =>
StorageManager.setNumericValue(slider.name, slider.value) StorageManager.setNumericValue(slider.name, slider.value)
); );
} }
resetFilters(): void { resetFilters(): void {
this.inputs.options.forEach((option) => { this.inputs.options.forEach((option) => {
option.value = option.defaultValue; option.value = option.defaultValue;
StorageManager.setBooleanValue(option.name, option.value); StorageManager.setBooleanValue(option.name, option.value);
}); });
this.inputs.sliders.forEach((slider) => { this.inputs.sliders.forEach((slider) => {
slider.value = slider.defaultValue; slider.value = slider.defaultValue;
StorageManager.setNumericValue(slider.name, slider.value); StorageManager.setNumericValue(slider.name, slider.value);
}); });
this.$emit("resetFilters"); this.$emit("resetFilters");
} }
closeCard(): void { closeCard(): void {
this.exit(); this.exit();
} }
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import "../../styles/responsive"; @import "../../styles/responsive";
@import "../../styles/variables"; @import "../../styles/variables";
.filter-card { .filter-card {
position: fixed; position: fixed;
top: 50%; top: 50%;
left: 50%; left: 50%;
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
z-index: 3; z-index: 3;
overflow: auto; overflow: auto;
max-height: 95vh; max-height: 95vh;
padding: 0.5em; padding: 0.5em;
max-width: 600px; max-width: 600px;
width: 65%; width: 65%;
background: #262a2e; background: #262a2e;
font-size: calc(0.75rem + 0.45vw); font-size: calc(0.75rem + 0.45vw);
box-shadow: 0 0 15px 5px #474747; box-shadow: 0 0 15px 5px #474747;
@include smallScreen() { @include smallScreen() {
width: 100%; width: 100%;
font-size: calc(0.7em + 1.1vw); font-size: calc(0.7em + 1.1vw);
} }
@include bigScreen { @include bigScreen {
font-size: 1.1rem; font-size: 1.1rem;
} }
} }
.card { .card {
&-title { &-title {
font-size: 2em; font-size: 2em;
font-weight: 700; font-weight: 700;
color: $accentCol; color: $accentCol;
margin: 0.5em 0; margin: 0.5em 0;
margin-top: 1em; margin-top: 1em;
} }
&-options { &-options {
display: grid; display: grid;
grid-template-columns: repeat(auto-fill, minmax(6em, 1fr)); grid-template-columns: repeat(auto-fill, minmax(6em, 1fr));
padding: 0 1.5em; padding: 0 1.5em;
} }
&-sliders { &-sliders {
margin-top: 1em; margin-top: 1em;
} }
&-actions { &-actions {
margin-top: 0.7em; margin-top: 0.7em;
button { button {
margin: 0 0.3em; margin: 0 0.3em;
} }
} }
&-save { &-save {
display: flex; display: flex;
justify-content: center; justify-content: center;
.option { .option {
width: 30%; width: 30%;
font-size: 0.9em; font-size: 0.9em;
} }
} }
&-exit { &-exit {
img { img {
width: 2em; width: 2em;
} }
} }
} }
.option { .option {
margin: 0.3em; margin: 0.3em;
&-input { &-input {
display: none; display: none;
} }
&-content { &-content {
user-select: none; user-select: none;
-moz-user-select: none; -moz-user-select: none;
-webkit-user-select: none; -webkit-user-select: none;
width: 100%; width: 100%;
text-align: center; text-align: center;
cursor: pointer; cursor: pointer;
padding: 0.6em 0.5em; padding: 0.6em 0.5em;
border-radius: 0.4em; border-radius: 0.4em;
font-size: 0.65em; font-size: 0.65em;
background-color: #333; background-color: #333;
display: inline-block; display: inline-block;
position: relative; position: relative;
transition: all 0.2s; transition: all 0.2s;
&.save { &.save {
font-size: 0.8em; font-size: 0.8em;
} }
&:not(.checked) { &:not(.checked) {
background-color: #585858; background-color: #585858;
&::before { &::before {
box-shadow: none; box-shadow: none;
} }
} }
&.checked { &.checked {
&.access { &.access {
background-color: #e03b07; background-color: #e03b07;
&::before { &::before {
box-shadow: 0 0 6px 1px #e03b07; box-shadow: 0 0 6px 1px #e03b07;
} }
} }
&.control { &.control {
background-color: #0085ff; background-color: #0085ff;
&::before { &::before {
box-shadow: 0 0 6px 1px #0085ff; box-shadow: 0 0 6px 1px #0085ff;
} }
} }
&.signals { &.signals {
background-color: #b000bf; background-color: #b000bf;
&::before { &::before {
box-shadow: 0 0 6px 1px #b000bf; box-shadow: 0 0 6px 1px #b000bf;
} }
} }
&.status { &.status {
background-color: #05b702; background-color: #05b702;
&::before { &::before {
box-shadow: 0 0 6px 1px #05b702; box-shadow: 0 0 6px 1px #05b702;
} }
} }
&.save { &.save {
background-color: #05b702; background-color: #05b702;
&::before { &::before {
box-shadow: 0 0 6px 1px #05b702; box-shadow: 0 0 6px 1px #05b702;
} }
} }
&::before { &::before {
position: absolute; position: absolute;
content: ""; content: "";
top: 0; top: 0;
left: 0; left: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
border-radius: inherit; border-radius: inherit;
} }
} }
} }
} }
.slider { .slider {
display: flex; display: flex;
padding: 0.5em; padding: 0.5em;
&-value { &-value {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
color: $accentCol; color: $accentCol;
margin-right: 0.3em; margin-right: 0.3em;
padding: 0.1em 0.2em; padding: 0.1em 0.2em;
font-size: 1.1em; font-size: 1.1em;
font-weight: 500; font-weight: 500;
border-radius: 0.2em; border-radius: 0.2em;
} }
&-content { &-content {
display: flex; display: flex;
align-items: center; align-items: center;
font-size: 0.75em; font-size: 0.75em;
} }
&-input { &-input {
-webkit-appearance: none; -webkit-appearance: none;
appearance: none; appearance: none;
background: none; background: none;
border: none; border: none;
outline: none; outline: none;
min-width: 25%; min-width: 25%;
&::-webkit-slider-thumb { &::-webkit-slider-thumb {
-webkit-appearance: none; -webkit-appearance: none;
height: 20px; height: 20px;
width: 20px; width: 20px;
margin-top: -7px; margin-top: -7px;
border-radius: 50%; border-radius: 50%;
background: white; background: white;
border: 4px solid $accentCol; border: 4px solid $accentCol;
@include smallScreen() { @include smallScreen() {
width: 15px; width: 15px;
height: 15px; height: 15px;
margin-top: -5px; margin-top: -5px;
border: 3px solid $accentCol; border: 3px solid $accentCol;
} }
} }
&::-moz-range-thumb { &::-moz-range-thumb {
height: 15px; height: 15px;
width: 15px; width: 15px;
border-radius: 50%; border-radius: 50%;
background: white; background: white;
border: 4px solid $accentCol; border: 4px solid $accentCol;
cursor: pointer; cursor: pointer;
@include smallScreen() { @include smallScreen() {
width: 15px; width: 15px;
height: 15px; height: 15px;
border: 3px solid $accentCol; border: 3px solid $accentCol;
} }
} }
&::-webkit-slider-runnable-track { &::-webkit-slider-runnable-track {
width: 100%; width: 100%;
height: 5px; height: 5px;
cursor: pointer; cursor: pointer;
background: #ffffff; background: #ffffff;
border-radius: 1em; border-radius: 1em;
} }
&::-moz-range-track { &::-moz-range-track {
width: 100%; width: 100%;
height: 5px; height: 5px;
cursor: pointer; cursor: pointer;
background: #ffffff; background: #ffffff;
border-radius: 1em; border-radius: 1em;
} }
&::-ms-track { &::-ms-track {
width: 100%; width: 100%;
height: 5px; height: 5px;
cursor: pointer; cursor: pointer;
background: #ffffff; background: #ffffff;
border-radius: 1em; border-radius: 1em;
} }
} }
} }
</style> </style>
+140 -140
View File
@@ -1,141 +1,141 @@
<template> <template>
<div class="options"> <div class="options">
<div class="options-actions"> <div class="options-actions">
<button <button
class="action-btn" class="action-btn"
:class="{'open': filterCardOpen}" :class="{'open': filterCardOpen}"
@click="() => toggleCardsState('filter')" @click="() => toggleCardsState('filter')"
> >
<img :src="require('@/assets/icon-filter2.svg')" alt="icon-filter" /> <img :src="require('@/assets/icon-filter2.svg')" alt="icon-filter" />
<p>FILTRY</p> <p>FILTRY</p>
</button> </button>
<button <button
class="action-btn" class="action-btn"
:class="{'open': legendCardOpen}" :class="{'open': legendCardOpen}"
@click="() => toggleCardsState('legend')" @click="() => toggleCardsState('legend')"
> >
<img :src="require('@/assets/icon-legend.svg')" alt="icon legend" /> <img :src="require('@/assets/icon-legend.svg')" alt="icon legend" />
<p>LEGENDA</p> <p>LEGENDA</p>
</button> </button>
</div> </div>
<div class="options-content"> <div class="options-content">
<transition name="card-anim"></transition> <transition name="card-anim"></transition>
</div> </div>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { Vue, Component } from "vue-property-decorator"; import { Vue, Component } from "vue-property-decorator";
@Component({}) @Component({})
export default class Options extends Vue { export default class Options extends Vue {
filterCardOpen: boolean = false; filterCardOpen: boolean = false;
legendCardOpen: boolean = false; legendCardOpen: boolean = false;
toggleCardsState(name: string): void { toggleCardsState(name: string): void {
if (name == "filter") { if (name == "filter") {
this.legendCardOpen = false; this.legendCardOpen = false;
this.filterCardOpen = !this.filterCardOpen; this.filterCardOpen = !this.filterCardOpen;
} }
if (name == "legend") { if (name == "legend") {
this.filterCardOpen = false; this.filterCardOpen = false;
this.legendCardOpen = !this.legendCardOpen; this.legendCardOpen = !this.legendCardOpen;
} }
} }
toggleCardState(): void { toggleCardState(): void {
this.filterCardOpen = !this.filterCardOpen; this.filterCardOpen = !this.filterCardOpen;
} }
toggleLegendCardState(): void { toggleLegendCardState(): void {
this.legendCardOpen = !this.legendCardOpen; this.legendCardOpen = !this.legendCardOpen;
} }
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import "../../styles/variables.scss"; @import "../../styles/variables.scss";
@import "../../styles/responsive.scss"; @import "../../styles/responsive.scss";
.options { .options {
display: flex; display: flex;
z-index: 5; z-index: 5;
} }
.card-anim { .card-anim {
&-enter-active, &-enter-active,
&-leave-active { &-leave-active {
transition: all 0.25s ease-in-out; transition: all 0.25s ease-in-out;
} }
&-enter, &-enter,
&-leave-to { &-leave-to {
transform: translate(-45%, -50%); transform: translate(-45%, -50%);
opacity: 0; opacity: 0;
} }
} }
.options { .options {
font-size: calc(0.6rem + 0.9vw); font-size: calc(0.6rem + 0.9vw);
&-actions { &-actions {
display: flex; display: flex;
} }
} }
.action-btn { .action-btn {
display: flex; display: flex;
align-items: center; align-items: center;
background: #333; background: #333;
border: none; border: none;
color: #e0e0e0; color: #e0e0e0;
font-size: 0.75em; font-size: 0.75em;
padding: 0.3em; padding: 0.3em;
outline: none; outline: none;
cursor: pointer; cursor: pointer;
transition: all 0.3s; transition: all 0.3s;
img { img {
width: 1.3em; width: 1.3em;
margin-right: 0.2em; margin-right: 0.2em;
} }
p { p {
max-width: 0; max-width: 0;
font-size: 1em; font-size: 1em;
overflow: hidden; overflow: hidden;
transition: max-width 0.35s ease-in-out; transition: max-width 0.35s ease-in-out;
} }
&:hover > p, &:hover > p,
&.open > p { &.open > p {
max-width: 500px; max-width: 500px;
color: $accentCol; color: $accentCol;
} }
&:hover { &:hover {
background: rgba(#e0e0e0, 0.4); background: rgba(#e0e0e0, 0.4);
} }
} }
@include smallScreen { @include smallScreen {
.options { .options {
display: flex; display: flex;
justify-content: center; justify-content: center;
} }
.action-btn { .action-btn {
font-size: 0.8rem; font-size: 0.8rem;
} }
} }
</style> </style>
+425 -425
View File
@@ -1,426 +1,426 @@
<template> <template>
<section class="card station-card"> <section class="card station-card">
<div class="card-exit"> <div class="card-exit">
<img <img
class="schedule-icon" class="schedule-icon"
:src="require('@/assets/icon-clock.svg')" :src="require('@/assets/icon-clock.svg')"
alt="schedule-icon" alt="schedule-icon"
@click="() => (cardMode = cardMode == 0 ? 1 : 0)" @click="() => (cardMode = cardMode == 0 ? 1 : 0)"
/> />
<img :src="require('@/assets/icon-exit.svg')" alt="exit-icon" @click="exit" /> <img :src="require('@/assets/icon-exit.svg')" alt="exit-icon" @click="exit" />
</div> </div>
<div class="card-content" :class="{ offline: !stationInfo.online }"> <div class="card-content" :class="{ offline: !stationInfo.online }">
<div class="main"> <div class="main">
<div class="main-content"> <div class="main-content">
<span class="main-level flex" v-if="stationInfo.reqLevel > -1"> <span class="main-level flex" v-if="stationInfo.reqLevel > -1">
{{ {{
2 > parseInt(stationInfo.reqLevel) ? "L" : stationInfo.reqLevel 2 > parseInt(stationInfo.reqLevel) ? "L" : stationInfo.reqLevel
}} }}
</span> </span>
<span class="main-general"> <span class="main-general">
<div class="main-name"> <div class="main-name">
<a <a
v-if="stationInfo.stationURL" v-if="stationInfo.stationURL"
:href="stationInfo.stationURL" :href="stationInfo.stationURL"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
>{{ stationInfo.stationName }}</a> >{{ stationInfo.stationName }}</a>
<span v-else>{{ stationInfo.stationName }}</span> <span v-else>{{ stationInfo.stationName }}</span>
</div> </div>
<div class="main-hash">{{ stationInfo.stationHash }}</div> <div class="main-hash">{{ stationInfo.stationHash }}</div>
</span> </span>
</div> </div>
</div> </div>
<div class="icons"> <div class="icons">
<img <img
v-if="stationInfo.controlType" v-if="stationInfo.controlType"
:src="require(`@/assets/icon-${stationInfo.controlType}.svg`)" :src="require(`@/assets/icon-${stationInfo.controlType}.svg`)"
:alt="stationInfo.controlType" :alt="stationInfo.controlType"
:title="'Sterowanie ' + stationInfo.controlType" :title="'Sterowanie ' + stationInfo.controlType"
/> />
<img <img
v-if="stationInfo.signalType" v-if="stationInfo.signalType"
:src="require(`@/assets/icon-${stationInfo.signalType}.svg`)" :src="require(`@/assets/icon-${stationInfo.signalType}.svg`)"
:alt="stationInfo.signalType" :alt="stationInfo.signalType"
:title="'Sygnalizacja ' + stationInfo.signalType" :title="'Sygnalizacja ' + stationInfo.signalType"
/> />
<img <img
v-if="stationInfo.SBL && stationInfo.SBL !== ''" v-if="stationInfo.SBL && stationInfo.SBL !== ''"
:src="require(`@/assets/icon-SBL.svg`)" :src="require(`@/assets/icon-SBL.svg`)"
alt="SBL" alt="SBL"
title="Sceneria posiada SBL na przynajmniej jednym ze szlaków" title="Sceneria posiada SBL na przynajmniej jednym ze szlaków"
/> />
<img <img
v-if="stationInfo.default" v-if="stationInfo.default"
:src="require(`@/assets/icon-td2.svg`)" :src="require(`@/assets/icon-td2.svg`)"
alt="default-pack" alt="default-pack"
title="Sceneria domyślnie dostępna w grze" title="Sceneria domyślnie dostępna w grze"
/> />
<img <img
v-if="stationInfo.nonPublic || !stationInfo.reqLevel" v-if="stationInfo.nonPublic || !stationInfo.reqLevel"
:src="require(`@/assets/icon-lock.svg`)" :src="require(`@/assets/icon-lock.svg`)"
alt="non-public" alt="non-public"
title="Sceneria niepubliczna" title="Sceneria niepubliczna"
/> />
<img <img
v-if="stationInfo.unavailable" v-if="stationInfo.unavailable"
:src="require(`@/assets/icon-unavailable.svg`)" :src="require(`@/assets/icon-unavailable.svg`)"
alt="icon-unavailable" alt="icon-unavailable"
title="Sceneria niedostępna" title="Sceneria niedostępna"
/> />
</div> </div>
<div class="dispatcher"> <div class="dispatcher">
<div <div
class="dispatcher-level flex" class="dispatcher-level flex"
:style=" :style="
calculateExpStyle( calculateExpStyle(
stationInfo.dispatcherExp, stationInfo.dispatcherExp,
stationInfo.dispatcherIsSupporter stationInfo.dispatcherIsSupporter
) )
" "
>{{ stationInfo.online ? computedDispatcherExp : "" }}</div> >{{ stationInfo.online ? computedDispatcherExp : "" }}</div>
<div class="dispatcher-info"> <div class="dispatcher-info">
<div class="dispatcher-name"> <div class="dispatcher-name">
<a <a
:href=" :href="
'https://td2.info.pl/profile/?u=' + stationInfo.dispatcherId 'https://td2.info.pl/profile/?u=' + stationInfo.dispatcherId
" "
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
>{{ stationInfo.dispatcherName || "---" }}</a> >{{ stationInfo.dispatcherName || "---" }}</a>
</div> </div>
<div class="dispatcher-rate"> <div class="dispatcher-rate">
<img :src="require(`@/assets/icon-like.svg`)" alt="like-icon" /> <img :src="require(`@/assets/icon-like.svg`)" alt="like-icon" />
<span>{{ stationInfo.dispatcherRate }}</span> <span>{{ stationInfo.dispatcherRate }}</span>
</div> </div>
</div> </div>
</div> </div>
<div class="hours"> <div class="hours">
<div class="hours-title title">STATUS</div> <div class="hours-title title">STATUS</div>
<span class="status" :class="statusClasses(stationInfo.occupiedTo)"> <span class="status" :class="statusClasses(stationInfo.occupiedTo)">
{{ {{
stationInfo.occupiedTo stationInfo.occupiedTo
}} }}
</span> </span>
</div> </div>
<div class="spawns flex flex-column"> <div class="spawns flex flex-column">
<h3 class="spawns-title title">OTWARTE SPAWNY</h3> <h3 class="spawns-title title">OTWARTE SPAWNY</h3>
<div class="spawns-content"> <div class="spawns-content">
<span <span
class="spawn" class="spawn"
v-for="(spawn, i) in stationInfo.spawnString" v-for="(spawn, i) in stationInfo.spawnString"
:key="spawn + stationInfo.dispatcherName + i" :key="spawn + stationInfo.dispatcherName + i"
>{{ spawn }}</span> >{{ spawn }}</span>
<span class="spawn" v-if="!stationInfo.spawnString">BRAK</span> <span class="spawn" v-if="!stationInfo.spawnString">BRAK</span>
</div> </div>
</div> </div>
<div class="users flex flex-column"> <div class="users flex flex-column">
<h3 class="users-title title">GRACZE NA STACJI</h3> <h3 class="users-title title">GRACZE NA STACJI</h3>
<div class="users-content"> <div class="users-content">
<div <div
class="user-badge" class="user-badge"
:class="train.stopStatus" :class="train.stopStatus"
v-for="train in computedStationTrains" v-for="train in computedStationTrains"
:key="train.trainNo + train.driverName" :key="train.trainNo + train.driverName"
> >
<router-link <router-link
:to="{ :to="{
name: 'TrainsView', name: 'TrainsView',
params: { passedSearchedTrain: train.trainNo.toString() }, params: { passedSearchedTrain: train.trainNo.toString() },
}" }"
> >
<span>{{ train.trainNo }}</span> <span>{{ train.trainNo }}</span>
| |
<span>{{ train.driverName }}</span> <span>{{ train.driverName }}</span>
</router-link> </router-link>
</div> </div>
<span <span
class="user borderless" class="user borderless"
v-if=" v-if="
!stationInfo.stationTrains || !stationInfo.stationTrains ||
stationInfo.stationTrains.length == 0 stationInfo.stationTrains.length == 0
" "
>BRAK</span> >BRAK</span>
</div> </div>
</div> </div>
</div> </div>
<StationTimetable <StationTimetable
:class="{ show: cardMode == 1 }" :class="{ show: cardMode == 1 }"
:scheduledTrains="this.stationInfo.scheduledTrains" :scheduledTrains="this.stationInfo.scheduledTrains"
:stationName="stationInfo.stationName" :stationName="stationInfo.stationName"
/> />
</section> </section>
</template> </template>
<script lang="ts"> <script lang="ts">
import { Component, Prop, Watch } from "vue-property-decorator"; import { Component, Prop, Watch } from "vue-property-decorator";
import { Getter } from "vuex-class"; import { Getter } from "vuex-class";
import styleMixin from "@/mixins/styleMixin"; import styleMixin from "@/mixins/styleMixin";
import Station from "@/scripts/interfaces/Station"; import Station from "@/scripts/interfaces/Station";
import Train from "@/scripts/interfaces/Train"; import Train from "@/scripts/interfaces/Train";
import StationTimetable from "@/components/StationsView/StationTimetable.vue"; import StationTimetable from "@/components/StationsView/StationTimetable.vue";
@Component({ @Component({
components: { components: {
StationTimetable StationTimetable
} }
}) })
export default class StationCard extends styleMixin { export default class StationCard extends styleMixin {
@Prop() stationInfo!: Station; @Prop() stationInfo!: Station;
@Prop() exit!: void; @Prop() exit!: void;
cardMode: number = 0; cardMode: number = 0;
get computedDispatcherExp(): string { get computedDispatcherExp(): string {
return this.stationInfo.dispatcherExp < 2 return this.stationInfo.dispatcherExp < 2
? "L" ? "L"
: `${this.stationInfo.dispatcherExp}`; : `${this.stationInfo.dispatcherExp}`;
} }
get computedStationTrains() { get computedStationTrains() {
return this.stationInfo.stationTrains.map(stationTrain => { return this.stationInfo.stationTrains.map(stationTrain => {
const scheduledData = this.stationInfo.scheduledTrains.find(scheduledTrain => scheduledTrain.trainNo === stationTrain.trainNo); const scheduledData = this.stationInfo.scheduledTrains.find(scheduledTrain => scheduledTrain.trainNo === stationTrain.trainNo);
return { return {
...stationTrain, ...stationTrain,
stopStatus: scheduledData?.stopStatus || "no-timetable" stopStatus: scheduledData?.stopStatus || "no-timetable"
} }
}) })
} }
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import "../../styles/variables.scss"; @import "../../styles/variables.scss";
@import "../../styles/responsive.scss"; @import "../../styles/responsive.scss";
@import "../../styles/user_badge.scss"; @import "../../styles/user_badge.scss";
.title { .title {
color: $accentCol; color: $accentCol;
font-weight: 600; font-weight: 600;
margin: 0.5em 0; margin: 0.5em 0;
} }
.card { .card {
padding: 2em; padding: 2em;
text-align: center; text-align: center;
font-size: calc(0.5rem + 0.4vw); font-size: calc(0.5rem + 0.4vw);
max-width: 800px; max-width: 800px;
@include bigScreen { @include bigScreen {
font-size: 1rem; font-size: 1rem;
} }
@include smallScreen { @include smallScreen {
font-size: 0.8em; font-size: 0.8em;
width: 100%; width: 100%;
} }
user-select: none; user-select: none;
-moz-user-select: none; -moz-user-select: none;
-webkit-user-select: none; -webkit-user-select: none;
&-exit { &-exit {
z-index: 3; z-index: 3;
display: flex; display: flex;
align-items: center; align-items: center;
img { img {
font-size: 1.6em; font-size: 1.6em;
margin: 0.1em 0.1em; margin: 0.1em 0.1em;
} }
.schedule-icon { .schedule-icon {
font-size: 1.4em; font-size: 1.4em;
} }
} }
&-content { &-content {
position: relative; position: relative;
margin-top: 1rem; margin-top: 1rem;
display: grid; display: grid;
grid-template-areas: "main main" "icons icons" "dispatcher hours" "users spawns"; grid-template-areas: "main main" "icons icons" "dispatcher hours" "users spawns";
grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-columns: repeat(2, minmax(0, 1fr));
min-width: 200px; min-width: 200px;
max-height: 600px; max-height: 600px;
transform: translateY(0%); transform: translateY(0%);
gap: 1.5em; gap: 1.5em;
&.offline { &.offline {
.users, .users,
.spawns, .spawns,
.dispatcher { .dispatcher {
filter: grayscale(1); filter: grayscale(1);
opacity: 0.5; opacity: 0.5;
} }
} }
@include smallScreen() { @include smallScreen() {
grid-template-areas: "main main" "icons icons" "dispatcher dispatcher" "hours hours" "users users" "spawns spawns"; grid-template-areas: "main main" "icons icons" "dispatcher dispatcher" "hours hours" "users users" "spawns spawns";
} }
} }
} }
.main { .main {
grid-area: main; grid-area: main;
text-align: center; text-align: center;
&-content { &-content {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
@include smallScreen() { @include smallScreen() {
flex-direction: column; flex-direction: column;
} }
} }
&-level { &-level {
background: $accentCol; background: $accentCol;
color: black; color: black;
font-size: 3em; font-size: 3em;
font-weight: 600; font-weight: 600;
border-radius: 50%; border-radius: 50%;
width: 1.7em; width: 1.7em;
height: 1.7em; height: 1.7em;
margin: 0.3em 0.5em; margin: 0.3em 0.5em;
} }
&-hash { &-hash {
color: #9d9d9d; color: #9d9d9d;
} }
&-name { &-name {
color: $accentCol; color: $accentCol;
font-weight: 600; font-weight: 600;
font-size: 2.3em; font-size: 2.3em;
text-transform: uppercase; text-transform: uppercase;
} }
} }
.icons { .icons {
grid-area: icons; grid-area: icons;
display: flex; display: flex;
justify-content: center; justify-content: center;
img { img {
max-width: 3em; max-width: 3em;
margin: 0 0.4em; margin: 0 0.4em;
} }
} }
.dispatcher { .dispatcher {
grid-area: dispatcher; grid-area: dispatcher;
display: flex; display: flex;
justify-content: center; justify-content: center;
&-level { &-level {
font-size: 2.5em; font-size: 2.5em;
font-weight: bold; font-weight: bold;
margin-right: 0.3em; margin-right: 0.3em;
max-width: 2em; max-width: 2em;
background: forestgreen; background: forestgreen;
} }
&-info { &-info {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
flex-direction: column; flex-direction: column;
padding: 0.5em; padding: 0.5em;
} }
&-name { &-name {
font-size: 1.35em; font-size: 1.35em;
font-weight: bold; font-weight: bold;
padding-bottom: 0.5em; padding-bottom: 0.5em;
} }
&-rate { &-rate {
display: flex; display: flex;
font-size: 1.3em; font-size: 1.3em;
span { span {
margin: 0 0.3em; margin: 0 0.3em;
color: $accentCol; color: $accentCol;
} }
img { img {
width: 1.2em; width: 1.2em;
} }
} }
} }
.hours { .hours {
grid-area: hours; grid-area: hours;
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
flex-direction: column; flex-direction: column;
.status { .status {
font-size: 1.1em; font-size: 1.1em;
} }
} }
.users { .users {
grid-area: users; grid-area: users;
&-content { &-content {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
justify-content: center; justify-content: center;
} }
} }
.spawns { .spawns {
grid-area: spawns; grid-area: spawns;
overflow: hidden; overflow: hidden;
&-content { &-content {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
justify-content: center; justify-content: center;
& > .spawn { & > .spawn {
padding: 0.3em 0.4em; padding: 0.3em 0.4em;
margin: 0.3em; margin: 0.3em;
background: #585858; background: #585858;
text-align: center; text-align: center;
} }
} }
} }
</style> </style>
+394 -394
View File
@@ -1,395 +1,395 @@
<template> <template>
<section class="station_table"> <section class="station_table">
<div class="table_wrapper"> <div class="table_wrapper">
<table> <table>
<thead> <thead>
<tr> <tr>
<th v-for="(head, i) in headTitles" :key="i" @click="() => changeSorter(i)"> <th v-for="(head, i) in headTitles" :key="i" @click="() => changeSorter(i)">
<span class="header_wrapper"> <span class="header_wrapper">
<div class="header_item"> <div class="header_item">
<div v-if="head[0].includes('.svg')"> <div v-if="head[0].includes('.svg')">
<img :src="head[0]" alt="test" :title="head[1]" /> <img :src="head[0]" alt="test" :title="head[1]" />
</div> </div>
<div v-else> <div v-else>
<div>{{ head[0] }}</div> <div>{{ head[0] }}</div>
<div v-if="head.length > 1">{{ head[1] }}</div> <div v-if="head.length > 1">{{ head[1] }}</div>
</div> </div>
</div> </div>
<img <img
class="sort-icon" class="sort-icon"
v-if="sorterActive.index == i" v-if="sorterActive.index == i"
:src="sorterActive.dir == 1 ? ascIcon : descIcon" :src="sorterActive.dir == 1 ? ascIcon : descIcon"
alt alt
/> />
</span> </span>
</th> </th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr <tr
class="station" class="station"
v-for="(station, i) in stations" v-for="(station, i) in stations"
:key="i + station.stationHash" :key="i + station.stationHash"
@click="() => setScenery(station.stationName)" @click="() => setScenery(station.stationName)"
> >
<td <td
class="station_name" class="station_name"
:class="{ :class="{
'default-station': station.default, 'default-station': station.default,
online: station.online, online: station.online,
'station-unavailable': station.unavailable, 'station-unavailable': station.unavailable,
}" }"
>{{ station.stationName }}</td> >{{ station.stationName }}</td>
<td class="station_level"> <td class="station_level">
<span <span
v-if="station.reqLevel" v-if="station.reqLevel"
:style="calculateExpStyle(station.reqLevel)" :style="calculateExpStyle(station.reqLevel)"
>{{ station.reqLevel && station.reqLevel > -1 ? parseInt(station.reqLevel) >= 2 ? station.reqLevel : "L" : "?" }}</span> >{{ station.reqLevel && station.reqLevel > -1 ? parseInt(station.reqLevel) >= 2 ? station.reqLevel : "L" : "?" }}</span>
<span v-else>?</span> <span v-else>?</span>
</td> </td>
<td class="station_status"> <td class="station_status">
<span <span
class="status" class="status"
:class="statusClasses(station.occupiedTo)" :class="statusClasses(station.occupiedTo)"
>{{ station.occupiedTo}}</span> >{{ station.occupiedTo}}</span>
</td> </td>
<td class="station_dispatcher-name">{{ station.online ? station.dispatcherName : "" }}</td> <td class="station_dispatcher-name">{{ station.online ? station.dispatcherName : "" }}</td>
<td class="station_dispatcher-exp"> <td class="station_dispatcher-exp">
<span <span
v-if="station.online" v-if="station.online"
:style="calculateExpStyle(station.dispatcherExp)" :style="calculateExpStyle(station.dispatcherExp)"
>{{ 2 > station.dispatcherExp ? "L" : station.dispatcherExp }}</span> >{{ 2 > station.dispatcherExp ? "L" : station.dispatcherExp }}</span>
</td> </td>
<td class="station_tracks twoway"> <td class="station_tracks twoway">
<span <span
v-if="station.routes && station.routes.twoWay.catenary > 0" v-if="station.routes && station.routes.twoWay.catenary > 0"
class="track catenary" class="track catenary"
:title="`Liczba zelektryfikowanych szlaków dwutorowych: ${station.routes.twoWay.catenary}`" :title="`Liczba zelektryfikowanych szlaków dwutorowych: ${station.routes.twoWay.catenary}`"
>{{ station.routes.twoWay.catenary }}</span> >{{ station.routes.twoWay.catenary }}</span>
<span <span
v-if="station.routes && station.routes.twoWay.noCatenary > 0" v-if="station.routes && station.routes.twoWay.noCatenary > 0"
class="track no-catenary" class="track no-catenary"
:title="`Liczba niezelektryfikowanych szlaków dwutorowych: ${station.routes.twoWay.noCatenary}`" :title="`Liczba niezelektryfikowanych szlaków dwutorowych: ${station.routes.twoWay.noCatenary}`"
>{{ station.routes.twoWay.noCatenary }}</span> >{{ station.routes.twoWay.noCatenary }}</span>
<span class="separator"></span> <span class="separator"></span>
<span <span
v-if="station.routes && station.routes.oneWay.catenary > 0" v-if="station.routes && station.routes.oneWay.catenary > 0"
class="track catenary" class="track catenary"
:title="`Liczba zelektryfikowanych szlaków jednotorowych: ${station.routes.oneWay.catenary}`" :title="`Liczba zelektryfikowanych szlaków jednotorowych: ${station.routes.oneWay.catenary}`"
>{{ station.routes.oneWay.catenary }}</span> >{{ station.routes.oneWay.catenary }}</span>
<span <span
v-if="station.routes && station.routes.oneWay.noCatenary > 0" v-if="station.routes && station.routes.oneWay.noCatenary > 0"
class="track no-catenary" class="track no-catenary"
:title="`Liczba niezelektryfikowanych szlaków jednotorowych: ${station.routes.oneWay.noCatenary}`" :title="`Liczba niezelektryfikowanych szlaków jednotorowych: ${station.routes.oneWay.noCatenary}`"
>{{ station.routes.oneWay.noCatenary }}</span> >{{ station.routes.oneWay.noCatenary }}</span>
</td> </td>
<td class="station_info"> <td class="station_info">
<img <img
class="icon-info" class="icon-info"
v-if="station.controlType" v-if="station.controlType"
:src="require(`@/assets/icon-${station.controlType}.svg`)" :src="require(`@/assets/icon-${station.controlType}.svg`)"
:alt="station.controlType" :alt="station.controlType"
:title="'Sterowanie ' + station.controlType" :title="'Sterowanie ' + station.controlType"
/> />
<img <img
class="icon-info" class="icon-info"
v-if="station.signalType" v-if="station.signalType"
:src="require(`@/assets/icon-${station.signalType}.svg`)" :src="require(`@/assets/icon-${station.signalType}.svg`)"
:alt="station.signalType" :alt="station.signalType"
:title="'Sygnalizacja ' + station.signalType" :title="'Sygnalizacja ' + station.signalType"
/> />
<img <img
class="icon-info" class="icon-info"
v-if="station.SBL && station.SBL !== ''" v-if="station.SBL && station.SBL !== ''"
:src="require(`@/assets/icon-SBL.svg`)" :src="require(`@/assets/icon-SBL.svg`)"
alt="SBL" alt="SBL"
title="Sceneria posiada SBL na przynajmniej jednym ze szlaków" title="Sceneria posiada SBL na przynajmniej jednym ze szlaków"
/> />
<img <img
class="icon-info" class="icon-info"
v-if="!station.reqLevel || station.nonPublic" v-if="!station.reqLevel || station.nonPublic"
:src="require(`@/assets/icon-lock.svg`)" :src="require(`@/assets/icon-lock.svg`)"
alt="non-public" alt="non-public"
title="Sceneria niepubliczna" title="Sceneria niepubliczna"
/> />
</td> </td>
<td class="station_users" :class="{inactive: !station.online }"> <td class="station_users" :class="{inactive: !station.online }">
<span> <span>
<span class="highlight">{{ station.currentUsers }}</span> <span class="highlight">{{ station.currentUsers }}</span>
/ /
<span>{{ station.maxUsers }}</span> <span>{{ station.maxUsers }}</span>
</span> </span>
</td> </td>
<td class="station_spawns" :class="{inactive: !station.online }"> <td class="station_spawns" :class="{inactive: !station.online }">
<span class="highlight">{{ station.spawns.length }}</span> <span class="highlight">{{ station.spawns.length }}</span>
</td> </td>
<td class="station_schedules" :class="{inactive: !station.online }"> <td class="station_schedules" :class="{inactive: !station.online }">
<span class="highlight">{{station.scheduledTrains.length}} &nbsp;</span> <span class="highlight">{{station.scheduledTrains.length}} &nbsp;</span>
/ /
<span <span
style="color: #bbb" style="color: #bbb"
>{{ station.scheduledTrains.filter(train => train.stopInfo.confirmed).length }}</span> >{{ station.scheduledTrains.filter(train => train.stopInfo.confirmed).length }}</span>
</td> </td>
<!-- <!--
<td class="station_stats"> <td class="station_stats">
<div class="stats_wrapper"></div> <div class="stats_wrapper"></div>
</td>--> </td>-->
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div> </div>
<div class="no-stations" v-if="stations.length == 0">Ups! Brak stacji do wyświetlenia!</div> <div class="no-stations" v-if="stations.length == 0">Ups! Brak stacji do wyświetlenia!</div>
</section> </section>
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue from "vue"; import Vue from "vue";
import { Component, Prop } from "vue-property-decorator"; import { Component, Prop } from "vue-property-decorator";
import Station from "@/scripts/interfaces/Station"; import Station from "@/scripts/interfaces/Station";
import styleMixin from "@/mixins/styleMixin"; import styleMixin from "@/mixins/styleMixin";
import Options from "@/components/StationsView/Options.vue"; import Options from "@/components/StationsView/Options.vue";
@Component({ @Component({
components: { Options }, components: { Options },
}) })
export default class StationTable extends styleMixin { export default class StationTable extends styleMixin {
@Prop() readonly stations!: Station[]; @Prop() readonly stations!: Station[];
@Prop() readonly sorterActive!: number; @Prop() readonly sorterActive!: number;
@Prop() readonly setFocusedStation!: () => void; @Prop() readonly setFocusedStation!: () => void;
@Prop() readonly changeSorter!: () => void; @Prop() readonly changeSorter!: () => void;
likeIcon: string = require("@/assets/icon-like.svg"); likeIcon: string = require("@/assets/icon-like.svg");
spawnIcon: string = require("@/assets/icon-spawn.svg"); spawnIcon: string = require("@/assets/icon-spawn.svg");
timetableIcon: string = require("@/assets/icon-timetable.svg"); timetableIcon: string = require("@/assets/icon-timetable.svg");
userIcon: string = require("@/assets/icon-user.svg"); userIcon: string = require("@/assets/icon-user.svg");
trainIcon: string = require("@/assets/icon-train.svg"); trainIcon: string = require("@/assets/icon-train.svg");
ascIcon: string = require("@/assets/icon-arrow-asc.svg"); ascIcon: string = require("@/assets/icon-arrow-asc.svg");
descIcon: string = require("@/assets/icon-arrow-desc.svg"); descIcon: string = require("@/assets/icon-arrow-desc.svg");
headTitles: string[][] = [ headTitles: string[][] = [
["Stacja"], ["Stacja"],
["Min. poziom", "dyżurnego"], ["Min. poziom", "dyżurnego"],
["Status"], ["Status"],
["Dyżurny"], ["Dyżurny"],
["Poziom", "dyżurnego"], ["Poziom", "dyżurnego"],
["Szlaki", "2tor | 1tor"], ["Szlaki", "2tor | 1tor"],
["Informacje", "ogólne"], ["Informacje", "ogólne"],
[this.userIcon, "Mechanicy online"], [this.userIcon, "Mechanicy online"],
[this.spawnIcon, "Otwarte spawny"], [this.spawnIcon, "Otwarte spawny"],
[this.timetableIcon, "Aktywne RJ"], [this.timetableIcon, "Aktywne RJ"],
]; ];
setScenery(name: string) { setScenery(name: string) {
const station = this.stations.find( const station = this.stations.find(
(station) => station.stationName === name (station) => station.stationName === name
); );
if (!station) return; if (!station) return;
if (!station.online) { if (!station.online) {
window.location.href = station.stationURL; window.location.href = station.stationURL;
return; return;
} }
this.$router.push({ this.$router.push({
name: "SceneryView", name: "SceneryView",
query: { hash: station.stationHash }, query: { hash: station.stationHash },
}); });
} }
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import "../../styles/responsive.scss"; @import "../../styles/responsive.scss";
@import "../../styles/variables.scss"; @import "../../styles/variables.scss";
$rowCol: #4b4b4b; $rowCol: #4b4b4b;
.change-anim { .change-anim {
&-enter-active, &-enter-active,
&-leave-active { &-leave-active {
transition: opacity 100ms ease-in; transition: opacity 100ms ease-in;
} }
&-enter, &-enter,
&-leave-to { &-leave-to {
opacity: 0; opacity: 0;
} }
} }
.highlight { .highlight {
color: gold; color: gold;
} }
section.station_table { section.station_table {
overflow: auto; overflow: auto;
overflow-y: hidden; overflow-y: hidden;
font-size: calc(0.55rem + 0.35vw); font-size: calc(0.55rem + 0.35vw);
font-weight: 500; font-weight: 500;
@include smallScreen() { @include smallScreen() {
font-size: 0.6rem; font-size: 0.6rem;
} }
} }
.table_wrapper { .table_wrapper {
overflow: auto; overflow: auto;
} }
table { table {
white-space: nowrap; white-space: nowrap;
border-collapse: collapse; border-collapse: collapse;
min-width: 1000px; min-width: 1000px;
thead th { thead th {
position: sticky; position: sticky;
top: 0; top: 0;
min-width: 85px; min-width: 85px;
padding: 0.5em; padding: 0.5em;
background-color: $primaryCol; background-color: $primaryCol;
cursor: pointer; cursor: pointer;
user-select: none; user-select: none;
-moz-user-select: none; -moz-user-select: none;
-webkit-user-select: none; -webkit-user-select: none;
span { span {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
img { img {
width: 1.5em; width: 1.5em;
vertical-align: middle; vertical-align: middle;
} }
} }
} }
} }
tr.station { tr.station {
background-color: $rowCol; background-color: $rowCol;
&:nth-child(even) { &:nth-child(even) {
background-color: lighten($rowCol, 5); background-color: lighten($rowCol, 5);
color: white; color: white;
} }
&:hover, &:hover,
&:focus { &:focus {
background-color: lighten($rowCol, 20); background-color: lighten($rowCol, 20);
} }
& > td { & > td {
padding: 0.3rem 1rem; padding: 0.3rem 1rem;
text-align: center; text-align: center;
cursor: pointer; cursor: pointer;
@include smallScreen() { @include smallScreen() {
margin: 0; margin: 0;
padding: 0.1rem 0.5rem; padding: 0.1rem 0.5rem;
} }
} }
} }
td.station { td.station {
&_level, &_level,
&_dispatcher-exp { &_dispatcher-exp {
span { span {
display: block; display: block;
width: 2em; width: 2em;
height: 2em; height: 2em;
line-height: 2em; line-height: 2em;
margin: 0 auto; margin: 0 auto;
} }
} }
&_level { &_level {
span { span {
background-color: #888; background-color: #888;
border-radius: 50%; border-radius: 50%;
} }
} }
&_info, &_info,
&_tracks { &_tracks {
img { img {
width: 2.2em; width: 2.2em;
margin: 0 0.2em; margin: 0 0.2em;
vertical-align: middle; vertical-align: middle;
} }
} }
&_tracks { &_tracks {
.no-catenary { .no-catenary {
background-color: #939393; background-color: #939393;
} }
.catenary { .catenary {
background-color: #009dce; background-color: #009dce;
} }
.track { .track {
margin: 0 0.3rem; margin: 0 0.3rem;
padding: 0.5em; padding: 0.5em;
} }
} }
&_users, &_users,
&_spawns, &_spawns,
&_schedules { &_schedules {
&.inactive { &.inactive {
opacity: 0.2; opacity: 0.2;
} }
} }
} }
.separator { .separator {
border-left: 3px solid #b3b3b3; border-left: 3px solid #b3b3b3;
} }
.no-stations { .no-stations {
text-align: center; text-align: center;
font-size: 1.5em; font-size: 1.5em;
padding: 1rem; padding: 1rem;
margin: 1rem 0; margin: 1rem 0;
background: #333; background: #333;
} }
.station-unavailable { .station-unavailable {
color: #ff1e1e; color: #ff1e1e;
font-weight: bold; font-weight: bold;
} }
</style> </style>
+284 -284
View File
@@ -1,285 +1,285 @@
<template> <template>
<div class="station-timetable"> <div class="station-timetable">
<div class="timetable-wrapper"> <div class="timetable-wrapper">
<div class="timetable-title title"> <div class="timetable-title title">
<div style="font-size: 1.5em">{{ stationName.toUpperCase() }}</div> <div style="font-size: 1.5em">{{ stationName.toUpperCase() }}</div>
<div style="font-size: 0.7em">AKTYWNE ROZKŁADY JAZDY</div> <div style="font-size: 0.7em">AKTYWNE ROZKŁADY JAZDY</div>
</div> </div>
<div class="timetable-content"> <div class="timetable-content">
<div <div
class="timetable-item" class="timetable-item"
v-for="(scheduledTrain, i) in computedScheduledTrains" v-for="(scheduledTrain, i) in computedScheduledTrains"
:key="i" :key="i"
> >
<span class="timetable-general"> <span class="timetable-general">
<span class="general-info"> <span class="general-info">
<router-link <router-link
:to="{ :to="{
name: 'TrainsView', name: 'TrainsView',
params: { params: {
passedSearchedTrain: scheduledTrain.trainNo.toString(), passedSearchedTrain: scheduledTrain.trainNo.toString(),
}, },
}" }"
> >
<span> <span>
<strong>{{ scheduledTrain.category }}</strong> <strong>{{ scheduledTrain.category }}</strong>
{{ scheduledTrain.trainNo }} {{ scheduledTrain.trainNo }}
</span> </span>
</router-link> </router-link>
| |
<span> <span>
<a <a
:href=" :href="
'https://td2.info.pl/profile/?u=' + scheduledTrain.driverId 'https://td2.info.pl/profile/?u=' + scheduledTrain.driverId
" "
target="_blank" target="_blank"
>{{ scheduledTrain.driverName }}</a >{{ scheduledTrain.driverName }}</a
> >
</span> </span>
</span> </span>
<span class="general-status"> <span class="general-status">
<span :class="scheduledTrain.stopStatus">{{ <span :class="scheduledTrain.stopStatus">{{
scheduledTrain.stopLabel scheduledTrain.stopLabel
}}</span> }}</span>
</span> </span>
</span> </span>
<span class="timetable-schedule"> <span class="timetable-schedule">
<span class="schedule-arrival"> <span class="schedule-arrival">
<span <span
class="arrival-time begins" class="arrival-time begins"
v-if="scheduledTrain.stopInfo.beginsHere" v-if="scheduledTrain.stopInfo.beginsHere"
>ROZPOCZYNA BIEG</span >ROZPOCZYNA BIEG</span
> >
<span class="arrival-time" v-else <span class="arrival-time" v-else
>{{ scheduledTrain.stopInfo.arrivalTimeString }} ({{ >{{ scheduledTrain.stopInfo.arrivalTimeString }} ({{
scheduledTrain.stopInfo.arrivalDelay scheduledTrain.stopInfo.arrivalDelay
}})</span }})</span
> >
</span> </span>
<span class="schedule-stop"> <span class="schedule-stop">
<span class="stop-time" v-if="scheduledTrain.stopInfo.stopTime" <span class="stop-time" v-if="scheduledTrain.stopInfo.stopTime"
>{{ scheduledTrain.stopInfo.stopTime }} >{{ scheduledTrain.stopInfo.stopTime }}
{{ scheduledTrain.stopInfo.stopType }}</span {{ scheduledTrain.stopInfo.stopType }}</span
> >
<span class="stop-arrow arrow"></span> <span class="stop-arrow arrow"></span>
</span> </span>
<span class="schedule-departure"> <span class="schedule-departure">
<span <span
class="departure-time terminates" class="departure-time terminates"
v-if="scheduledTrain.stopInfo.terminatesHere" v-if="scheduledTrain.stopInfo.terminatesHere"
>KOŃCZY BIEG</span >KOŃCZY BIEG</span
> >
<span class="departure-time" v-else <span class="departure-time" v-else
>{{ scheduledTrain.stopInfo.departureTimeString }} ({{ >{{ scheduledTrain.stopInfo.departureTimeString }} ({{
scheduledTrain.stopInfo.departureDelay scheduledTrain.stopInfo.departureDelay
}})</span }})</span
> >
</span> </span>
</span> </span>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { Component, Vue, Prop } from 'vue-property-decorator'; import { Component, Vue, Prop } from 'vue-property-decorator';
import Station from "@/scripts/interfaces/Station"; import Station from "@/scripts/interfaces/Station";
@Component @Component
export default class StationTimetable extends Vue { export default class StationTimetable extends Vue {
@Prop() readonly scheduledTrains; @Prop() readonly scheduledTrains;
@Prop() readonly stationName; @Prop() readonly stationName;
get computedScheduledTrains() { get computedScheduledTrains() {
return this.scheduledTrains.sort((a, b) => { return this.scheduledTrains.sort((a, b) => {
if (a.stopInfo.arrivalTimestamp > b.stopInfo.arrivalTimestamp) return 1; if (a.stopInfo.arrivalTimestamp > b.stopInfo.arrivalTimestamp) return 1;
else if ((a.stopInfo.arrivalTimestamp < b.stopInfo.arrivalTimestamp)) return -1; else if ((a.stopInfo.arrivalTimestamp < b.stopInfo.arrivalTimestamp)) return -1;
return a.stopInfo.departureTimestamp > b.stopInfo.departureTimestamp ? 1 : -1; return a.stopInfo.departureTimestamp > b.stopInfo.departureTimestamp ? 1 : -1;
}) })
} }
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import "../../styles/variables.scss"; @import "../../styles/variables.scss";
@import "../../styles/responsive.scss"; @import "../../styles/responsive.scss";
.station-timetable { .station-timetable {
position: absolute; position: absolute;
left: 0; left: 0;
top: 0; top: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
overflow: hidden; overflow: hidden;
transform: translateY(-100%); transform: translateY(-100%);
-webikit-transform: translateY(-100%); -webikit-transform: translateY(-100%);
&.show { &.show {
transform: translateY(0); transform: translateY(0);
-webkit-transform: translateY(0); -webkit-transform: translateY(0);
} }
transition: transform 150ms ease-out; transition: transform 150ms ease-out;
background: #333; background: #333;
@include smallScreen() { @include smallScreen() {
font-size: 1.3em; font-size: 1.3em;
} }
} }
.timetable { .timetable {
&-content { &-content {
width: 100%; width: 100%;
height: 100%; height: 100%;
overflow: auto; overflow: auto;
} }
&-title { &-title {
padding-top: 2rem; padding-top: 2rem;
padding-bottom: 0.3rem; padding-bottom: 0.3rem;
font-size: 1.6em; font-size: 1.6em;
} }
&-wrapper { &-wrapper {
height: 100%; height: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
&-item { &-item {
margin: 1em auto; margin: 1em auto;
display: grid; display: grid;
grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); grid-template-columns: repeat(auto-fit, minmax(0, 1fr));
padding: 0 1rem; padding: 0 1rem;
@include smallScreen() { @include smallScreen() {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
} }
} }
} }
.timetable { .timetable {
&-general { &-general {
padding: 0.3rem 0.7rem; padding: 0.3rem 0.7rem;
border: 2px solid white; border: 2px solid white;
border-radius: 10px; border-radius: 10px;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
@include smallScreen() { @include smallScreen() {
width: 95%; width: 95%;
font-size: 0.85em; font-size: 0.85em;
} }
} }
&-schedule { &-schedule {
@include smallScreen() { @include smallScreen() {
width: 80%; width: 80%;
margin: 0.7em 0; margin: 0.7em 0;
font-size: 0.9em; font-size: 0.9em;
} }
display: grid; display: grid;
grid-template-columns: repeat(auto-fit, minmax(50px, 1fr)); grid-template-columns: repeat(auto-fit, minmax(50px, 1fr));
font-size: 1.35em; font-size: 1.35em;
} }
} }
.arrow { .arrow {
border: solid white; border: solid white;
border-width: 0 2px 2px 0; border-width: 0 2px 2px 0;
display: inline-block; display: inline-block;
padding: 2px; padding: 2px;
margin-left: 50px; margin-left: 50px;
position: relative; position: relative;
transform: rotate(-45deg); transform: rotate(-45deg);
&::before { &::before {
content: ""; content: "";
position: absolute; position: absolute;
display: block; display: block;
width: 55px; width: 55px;
height: 3px; height: 3px;
top: 4px; top: 4px;
left: 4px; left: 4px;
transform: translate(-100%, -1px) rotate(45deg); transform: translate(-100%, -1px) rotate(45deg);
transform-origin: right bottom; transform-origin: right bottom;
background: white; background: white;
} }
} }
.general-info { .general-info {
span { span {
color: $accentCol; color: $accentCol;
} }
} }
.general-status { .general-status {
span.arriving { span.arriving {
color: #aaa; color: #aaa;
} }
span.departed { span.departed {
color: lime; color: lime;
} }
span.stopped { span.stopped {
color: #ffa600; color: #ffa600;
} }
span.online { span.online {
color: gold; color: gold;
} }
span.terminated { span.terminated {
color: red; color: red;
} }
} }
.schedule { .schedule {
&-arrival, &-arrival,
&-stop, &-stop,
&-departure { &-departure {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
margin: 0 0.3rem; margin: 0 0.3rem;
} }
&-stop { &-stop {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
.stop-time { .stop-time {
font-size: 0.7em; font-size: 0.7em;
} }
} }
} }
.arrival-time.begins, .arrival-time.begins,
.departure-time.terminates { .departure-time.terminates {
font-size: 0.75em; font-size: 0.75em;
} }
</style> </style>
+235 -235
View File
@@ -1,235 +1,235 @@
<template> <template>
<div class="train-schedule" @click="click"> <div class="train-schedule" @click="click">
<div class="schedule-wrapper"> <div class="schedule-wrapper">
<div class="schedule-bar"></div> <div class="schedule-bar"></div>
<ul class="schedule-list"> <ul class="schedule-list">
<li <li
class="schedule-item" class="schedule-item"
v-for="(stop, i) in followingStops" v-for="(stop, i) in followingStops"
:key="i" :key="i"
:class="{ confirmed: stop.confirmed, stopped: stop.stopped }" :class="{ confirmed: stop.confirmed, stopped: stop.stopped }"
> >
<div class="progress-bar"></div> <div class="progress-bar"></div>
<div <div
class="stop-line arrival" class="stop-line arrival"
v-if=" v-if="
i > 0 && followingStops[i - 1].departureLine != stop.arrivalLine i > 0 && followingStops[i - 1].departureLine != stop.arrivalLine
" "
>{{ stop.arrivalLine }}</div> >{{ stop.arrivalLine }}</div>
<span class="stop-info"> <span class="stop-info">
<div class="info-indicator"></div> <div class="info-indicator"></div>
<span class="info-name" v-html="stop.stopName"></span> <span class="info-name" v-html="stop.stopName"></span>
<span class="info-date"> <span class="info-date">
<span <span
class="date-arrival" class="date-arrival"
v-if="!stop.beginsHere" v-if="!stop.beginsHere"
:class="{ :class="{
delayed: stop.arrivalDelay > 0, delayed: stop.arrivalDelay > 0,
preponed: stop.arrivalDelay < 0, preponed: stop.arrivalDelay < 0,
}" }"
> >
p. p.
{{ {{
stylizeTime(stop.arrivalRealTimeString, stop.arrivalDelay) stylizeTime(stop.arrivalRealTimeString, stop.arrivalDelay)
}} }}
</span> </span>
<span <span
class="date-stop" class="date-stop"
v-if="stop.stopTime" v-if="stop.stopTime"
:class="stop.stopType.replace(', ', '-')" :class="stop.stopType.replace(', ', '-')"
>{{ stop.stopTime }} {{ stop.stopType }}</span> >{{ stop.stopTime }} {{ stop.stopType }}</span>
<span <span
class="date-departure" class="date-departure"
v-if="!stop.terminatesHere && stop.stopTime != 0" v-if="!stop.terminatesHere && stop.stopTime != 0"
:class="{ :class="{
delayed: stop.departureDelay > 0, delayed: stop.departureDelay > 0,
preponed: stop.departureDelay < 0, preponed: stop.departureDelay < 0,
}" }"
> >
o. o.
{{ {{
stylizeTime(stop.departureRealTimeString, stop.departureDelay) stylizeTime(stop.departureRealTimeString, stop.departureDelay)
}} }}
</span> </span>
</span> </span>
</span> </span>
<div class="stop-line departure">{{ stop.departureLine }}</div> <div class="stop-line departure">{{ stop.departureLine }}</div>
</li> </li>
</ul> </ul>
</div> </div>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { Component, Vue, Prop } from "vue-property-decorator"; import { Component, Vue, Prop } from "vue-property-decorator";
import TrainStop from "@/scripts/interfaces/TrainStop"; import TrainStop from "@/scripts/interfaces/TrainStop";
@Component @Component
export default class TrainSchedule extends Vue { export default class TrainSchedule extends Vue {
@Prop() readonly followingStops!: TrainStop[]; @Prop() readonly followingStops!: TrainStop[];
@Prop() readonly currentStationName!: string; @Prop() readonly currentStationName!: string;
stylizeTime(timeString: string, delay: number) { stylizeTime(timeString: string, delay: number) {
return ( return (
timeString + timeString +
(delay != 0 ? " (" + (delay > 0 ? "+" : "") + delay.toString() + ")" : "") (delay != 0 ? " (" + (delay > 0 ? "+" : "") + delay.toString() + ")" : "")
); );
} }
click() { click() {
this.$emit("click"); this.$emit("click");
} }
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import "../../styles/responsive.scss"; @import "../../styles/responsive.scss";
.train-schedule { .train-schedule {
max-height: 600px; max-height: 600px;
overflow: auto; overflow: auto;
margin-top: 1rem; margin-top: 1rem;
font-size: 1em; font-size: 1em;
@include smallScreen() { @include smallScreen() {
font-size: 0.8em; font-size: 0.8em;
} }
} }
.schedule-bar, .schedule-bar,
.progress-bar { .progress-bar {
position: absolute; position: absolute;
z-index: 1; z-index: 1;
width: 2px; width: 2px;
height: 100%; height: 100%;
top: 0; top: 0;
left: 0; left: 0;
background: white; background: white;
} }
.progress-bar { .progress-bar {
height: 100%; height: 100%;
top: 0; top: 0;
left: calc(-0.5rem - 2px); left: calc(-0.5rem - 2px);
width: 2px; width: 2px;
} }
.schedule-wrapper { .schedule-wrapper {
position: relative; position: relative;
margin-top: 1rem; margin-top: 1rem;
margin-left: 0.5rem; margin-left: 0.5rem;
} }
ul.schedule-list { ul.schedule-list {
margin-left: 10px; margin-left: 10px;
} }
ul.schedule-list > li.schedule-item { ul.schedule-list > li.schedule-item {
position: relative; position: relative;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
padding: 0 0.5rem; padding: 0 0.5rem;
&.confirmed { &.confirmed {
& > .progress-bar, & > .progress-bar,
& > .stop-info > .info-indicator { & > .stop-info > .info-indicator {
background: lime; background: lime;
} }
} }
&.stopped { &.stopped {
& > .progress-bar { & > .progress-bar {
background: lime; background: lime;
height: 50%; height: 50%;
} }
& > .stop-info > .info-indicator { & > .stop-info > .info-indicator {
background: orangered; background: orangered;
} }
} }
} }
li.schedule-item > .stop-info { li.schedule-item > .stop-info {
display: flex; display: flex;
position: relative; position: relative;
& > .info-indicator { & > .info-indicator {
position: absolute; position: absolute;
z-index: 1; z-index: 1;
top: 50%; top: 50%;
left: -1.5rem; left: -1.5rem;
transform: translateY(-50%); transform: translateY(-50%);
width: 15px; width: 15px;
height: 2px; height: 2px;
background: white; background: white;
} }
& > .info-name { & > .info-name {
background: rgb(0, 81, 187); background: rgb(0, 81, 187);
padding: 0.3rem 0.5rem; padding: 0.3rem 0.5rem;
} }
& > .info-date { & > .info-date {
display: flex; display: flex;
align-items: center; align-items: center;
& > span { & > span {
background: #5c5c5c; background: #5c5c5c;
padding: 0.3rem 0.5rem; padding: 0.3rem 0.5rem;
} }
& > .date-stop { & > .date-stop {
&.ph, &.ph,
&.ph-pm { &.ph-pm {
background: #ce8d00; background: #ce8d00;
} }
&.pt, &.pt,
&.pm, &.pm,
&.pt-pm { &.pt-pm {
background: #252525; background: #252525;
} }
} }
& > .date-arrival, & > .date-arrival,
& > .date-departure { & > .date-departure {
&.delayed { &.delayed {
background: rgb(250, 0, 0); background: rgb(250, 0, 0);
} }
&.preponed { &.preponed {
background: rgb(0, 139, 0); background: rgb(0, 139, 0);
} }
} }
} }
} }
li.schedule-item > .stop-line { li.schedule-item > .stop-line {
font-size: 0.8em; font-size: 0.8em;
color: #bbb; color: #bbb;
padding: 0.3em 0; padding: 0.3em 0;
margin: 0.2em 0; margin: 0.2em 0;
transform: translateX(-0.8rem); transform: translateX(-0.8rem);
} }
</style> </style>
+122 -122
View File
@@ -1,123 +1,123 @@
<template> <template>
<div class="train-search"> <div class="train-search">
<span class="search train"> <span class="search train">
<div class="search-title title">Szukaj składu</div> <div class="search-title title">Szukaj składu</div>
<div class="search-box"> <div class="search-box">
<input class="search-input" v-model="searchedTrain" /> <input class="search-input" v-model="searchedTrain" />
<img <img
class="search-exit" class="search-exit"
:src="exitIcon" :src="exitIcon"
alt="exit-icon" alt="exit-icon"
@click="() => (searchedTrain = '')" @click="() => (searchedTrain = '')"
/> />
</div> </div>
</span> </span>
<span class="search driver"> <span class="search driver">
<div class="search-title title">Szukaj maszynisty</div> <div class="search-title title">Szukaj maszynisty</div>
<div class="search-box"> <div class="search-box">
<input class="search-input" v-model="searchedDriver" /> <input class="search-input" v-model="searchedDriver" />
<img <img
class="search-exit" class="search-exit"
:src="exitIcon" :src="exitIcon"
alt="exit-icon" alt="exit-icon"
@click="() => (searchedDriver = '')" @click="() => (searchedDriver = '')"
/> />
</div> </div>
</span> </span>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { Component, Vue, Watch, Prop } from "vue-property-decorator"; import { Component, Vue, Watch, Prop } from "vue-property-decorator";
@Component @Component
export default class extends Vue { export default class extends Vue {
exitIcon = require("@/assets/icon-exit.svg"); exitIcon = require("@/assets/icon-exit.svg");
searchedTrain = ""; searchedTrain = "";
searchedDriver = ""; searchedDriver = "";
@Prop() readonly passedSearchedTrain!: string; @Prop() readonly passedSearchedTrain!: string;
@Prop() readonly focusedTrain!: string; @Prop() readonly focusedTrain!: string;
// @Prop() readonly passedSearchedDriver!: string; // @Prop() readonly passedSearchedDriver!: string;
@Watch("searchedTrain") @Watch("searchedTrain")
onSearchedTrainChanged(val: string, oldVal: string) { onSearchedTrainChanged(val: string, oldVal: string) {
this.$emit("changeSearchedTrain", val); this.$emit("changeSearchedTrain", val);
} }
@Watch("searchedDriver") @Watch("searchedDriver")
onSearchedDriverChanged(val: string, oldVal: string) { onSearchedDriverChanged(val: string, oldVal: string) {
this.$emit("changeSearchedDriver", val); this.$emit("changeSearchedDriver", val);
} }
@Watch("passedSearchedTrain") @Watch("passedSearchedTrain")
onPassedSearchedTrainChanged(val: string, oldVal: string) { onPassedSearchedTrainChanged(val: string, oldVal: string) {
if (val && val != "") { if (val && val != "") {
this.searchedTrain = val; this.searchedTrain = val;
this.searchedDriver = ""; this.searchedDriver = "";
} }
} }
@Watch("focusedTrain") @Watch("focusedTrain")
onFocusedTrainChanged(val: string, oldVal: string) { onFocusedTrainChanged(val: string, oldVal: string) {
console.log(val); console.log(val);
this.searchedTrain = val; this.searchedTrain = val;
this.searchedDriver = ""; this.searchedDriver = "";
} }
mounted() { mounted() {
if (this.passedSearchedTrain && this.passedSearchedTrain != "") { if (this.passedSearchedTrain && this.passedSearchedTrain != "") {
this.searchedTrain = this.passedSearchedTrain; this.searchedTrain = this.passedSearchedTrain;
this.searchedDriver = ""; this.searchedDriver = "";
} }
} }
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import "../../styles/responsive.scss"; @import "../../styles/responsive.scss";
.train-search { .train-search {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
justify-content: center; justify-content: center;
margin-bottom: 1rem; margin-bottom: 1rem;
} }
.search { .search {
padding-right: 1rem; padding-right: 1rem;
&-box { &-box {
position: relative; position: relative;
background: #333; background: #333;
border-radius: 0.5em; border-radius: 0.5em;
min-width: 150px; min-width: 150px;
} }
&-input { &-input {
border: none; border: none;
padding: 0.5rem 1rem; padding: 0.5rem 1rem;
margin: 0; margin: 0;
font-size: 1em; font-size: 1em;
min-width: 85%; min-width: 85%;
} }
&-exit { &-exit {
position: absolute; position: absolute;
cursor: pointer; cursor: pointer;
top: 50%; top: 50%;
right: 10px; right: 10px;
transform: translateY(-50%); transform: translateY(-50%);
width: 1em; width: 1em;
} }
} }
</style> </style>
+173 -173
View File
@@ -1,173 +1,173 @@
<template> <template>
<div class="train-sorter"> <div class="train-sorter">
<div class="sorter-wrapper"> <div class="sorter-wrapper">
<div class="sorter-box"> <div class="sorter-box">
<div class="title">Sortuj według</div> <div class="title">Sortuj według</div>
<div class="selected" @click="toggleOptionList"> <div class="selected" @click="toggleOptionList">
<span>{{ sorterName }}</span> <span>{{ sorterName }}</span>
<img :src="require('@/assets/icon-select.svg')" alt="icon-select" /> <img :src="require('@/assets/icon-select.svg')" alt="icon-select" />
</div> </div>
<div class="options-container"> <div class="options-container">
<ul class="options-list" :class="{ open: listOpen }"> <ul class="options-list" :class="{ open: listOpen }">
<li <li
class="option" class="option"
v-for="(option, i) in sortOptionList" v-for="(option, i) in sortOptionList"
:key="i" :key="i"
@click="() => chooseOption(option)" @click="() => chooseOption(option)"
> >
<input type="radio" name="sort" :id="option.id" /> <input type="radio" name="sort" :id="option.id" />
<label :for="option.id">{{ option.content }}</label> <label :for="option.id">{{ option.content }}</label>
</li> </li>
</ul> </ul>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { Vue, Component, Prop } from "vue-property-decorator"; import { Vue, Component, Prop } from "vue-property-decorator";
const ascSVG = require("@/assets/icon-arrow-asc.svg"); const ascSVG = require("@/assets/icon-arrow-asc.svg");
const descSVG = require("@/assets/icon-arrow-desc.svg"); const descSVG = require("@/assets/icon-arrow-desc.svg");
@Component @Component
export default class TrainSorter extends Vue { export default class TrainSorter extends Vue {
ascSVG = ascSVG; ascSVG = ascSVG;
descSVG = descSVG; descSVG = descSVG;
@Prop() trainList!: []; @Prop() trainList!: [];
listOpen: boolean = false; listOpen: boolean = false;
sorterName: string = "numer pociągu"; sorterName: string = "numer pociągu";
sortOptionList: { id: string; content: string }[] = [ sortOptionList: { id: string; content: string }[] = [
{ {
id: "mass", id: "mass",
content: "masa", content: "masa",
}, },
{ {
id: "speed", id: "speed",
content: "prędkość", content: "prędkość",
}, },
{ {
id: "length", id: "length",
content: "długość", content: "długość",
}, },
{ {
id: "distance", id: "distance",
content: "kilometraż", content: "kilometraż",
}, },
{ {
id: "timetable", id: "timetable",
content: "numer pociągu", content: "numer pociągu",
}, },
]; ];
toggleOptionList() { toggleOptionList() {
this.listOpen = !this.listOpen; this.listOpen = !this.listOpen;
} }
closeOptionList() { closeOptionList() {
this.listOpen = false; this.listOpen = false;
} }
chooseOption(option: { id: string; content: string }) { chooseOption(option: { id: string; content: string }) {
this.$emit("changeSorter", { id: option.id, dir: -1 }); this.$emit("changeSorter", { id: option.id, dir: -1 });
this.sorterName = option.content; this.sorterName = option.content;
this.closeOptionList(); this.closeOptionList();
} }
get compTrainList() { get compTrainList() {
return this.trainList; return this.trainList;
} }
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.title { .title {
padding-left: 0.3em; padding-left: 0.3em;
} }
.sorter-wrapper { .sorter-wrapper {
display: flex; display: flex;
user-select: none; user-select: none;
-moz-user-select: none; -moz-user-select: none;
-webkit-user-select: none; -webkit-user-select: none;
} }
.selected { .selected {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
span { span {
margin-right: 2rem; margin-right: 2rem;
} }
img { img {
max-width: 0.75em; max-width: 0.75em;
} }
} }
.selected, .selected,
.options-list { .options-list {
background: #333; background: #333;
border-radius: 0.5em; border-radius: 0.5em;
} }
.selected { .selected {
padding: 0.5rem 1rem; padding: 0.5rem 1rem;
min-width: 150px; min-width: 150px;
cursor: pointer; cursor: pointer;
} }
.options-container { .options-container {
position: relative; position: relative;
} }
.options-list { .options-list {
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
z-index: 10; z-index: 10;
width: 100%; width: 100%;
background-color: rgba(#222, 0.95); background-color: rgba(#222, 0.95);
overflow: hidden; overflow: hidden;
max-height: 0; max-height: 0;
&.open { &.open {
max-height: 250px; max-height: 250px;
opacity: 1; opacity: 1;
} }
transition: all 150ms ease-in; transition: all 150ms ease-in;
} }
.option { .option {
display: flex; display: flex;
&:hover { &:hover {
background-color: rgba(#868686, 0.85); background-color: rgba(#868686, 0.85);
} }
transition: background 150ms ease-in; transition: background 150ms ease-in;
} }
input { input {
display: none; display: none;
} }
label { label {
padding: 0.5rem 1rem; padding: 0.5rem 1rem;
width: 100%; width: 100%;
cursor: pointer; cursor: pointer;
} }
</style> </style>
+321 -321
View File
@@ -1,322 +1,322 @@
<template> <template>
<div class="train-stats"> <div class="train-stats">
<div class="btn-wrapper"> <div class="btn-wrapper">
<button <button
class="stats-btn button" class="stats-btn button"
@click="toggleStats" @click="toggleStats"
v-if="trains.length > 0" v-if="trains.length > 0"
> >
STATYSTYKI RUCHU STATYSTYKI RUCHU
</button> </button>
</div> </div>
<transition name="stats-anim"> <transition name="stats-anim">
<div class="stats-body" v-if="statsOpen"> <div class="stats-body" v-if="statsOpen">
<h2 class="stats-header">STATYSTYKI RUCHU</h2> <h2 class="stats-header">STATYSTYKI RUCHU</h2>
<div class="stats-speed"> <div class="stats-speed">
<div class="title stats-title"> <div class="title stats-title">
PRĘDKOŚCI POCIĄGÓW (MIN | ŚR | MAX) [km/h] PRĘDKOŚCI POCIĄGÓW (MIN | ŚR | MAX) [km/h]
</div> </div>
<div class="stats-content"> <div class="stats-content">
{{ speedStats.min }} | {{ speedStats.avg }} | {{ speedStats.max }} {{ speedStats.min }} | {{ speedStats.avg }} | {{ speedStats.max }}
</div> </div>
</div> </div>
<div class="stats-length"> <div class="stats-length">
<div class="title stats-title"> <div class="title stats-title">
DŁUGOŚCI ROZKŁADÓW (MIN | ŚR | MAX) [km] DŁUGOŚCI ROZKŁADÓW (MIN | ŚR | MAX) [km]
</div> </div>
<div class="stats-content"> <div class="stats-content">
{{ timetableStats.min }} | {{ timetableStats.avg }} | {{ timetableStats.min }} | {{ timetableStats.avg }} |
{{ timetableStats.max }} {{ timetableStats.max }}
</div> </div>
</div> </div>
<div class="stats-categories"> <div class="stats-categories">
<div class="title stats-title">KATEGORIE RJ</div> <div class="title stats-title">KATEGORIE RJ</div>
<div class="category-list"> <div class="category-list">
<span <span
class="category" class="category"
v-for="[key, value] of categoryList" v-for="[key, value] of categoryList"
:key="key" :key="key"
> >
<span class="category-type">{{ key }}</span> <span class="category-type">{{ key }}</span>
<span class="category-count">{{ value }}</span> <span class="category-count">{{ value }}</span>
</span> </span>
</div> </div>
<div class="special-list"> <div class="special-list">
<span class="special twr"> <span class="special twr">
<span class="special-type">WYSOKIEGO RYZYKA</span> <span class="special-type">WYSOKIEGO RYZYKA</span>
<span class="special-count">{{ specialTrainCount[0] }}</span> <span class="special-count">{{ specialTrainCount[0] }}</span>
</span> </span>
<span class="special skr"> <span class="special skr">
<span class="special-type">PRZEKROCZONA SKRAJNIA</span> <span class="special-type">PRZEKROCZONA SKRAJNIA</span>
<span class="special-count">{{ specialTrainCount[1] }}</span> <span class="special-count">{{ specialTrainCount[1] }}</span>
</span> </span>
</div> </div>
</div> </div>
<div class="stats-locos"> <div class="stats-locos">
<div class="title stats-title">NAJCZĘSTSZE JEDNOSTKI</div> <div class="title stats-title">NAJCZĘSTSZE JEDNOSTKI</div>
<div class="loco-list stats-content"> <div class="loco-list stats-content">
<div class="loco-item" v-for="(loco, i) in locoList" :key="i"> <div class="loco-item" v-for="(loco, i) in locoList" :key="i">
{{ loco[0] }} | {{ loco[1] }} {{ loco[0] }} | {{ loco[1] }}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</transition> </transition>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { Component, Vue, Prop } from "vue-property-decorator"; import { Component, Vue, Prop } from "vue-property-decorator";
import Train from "@/scripts/interfaces/Train"; import Train from "@/scripts/interfaces/Train";
@Component @Component
export default class TrainStats extends Vue { export default class TrainStats extends Vue {
@Prop() readonly trains!: Train[]; @Prop() readonly trains!: Train[];
statsOpen: boolean = false; statsOpen: boolean = false;
toggleStats() { toggleStats() {
this.statsOpen = !this.statsOpen; this.statsOpen = !this.statsOpen;
} }
get speedStats(): { avg: string; min: string; max: string } { get speedStats(): { avg: string; min: string; max: string } {
if (this.trains.length == 0) return { avg: "0", min: "0", max: "0" }; if (this.trains.length == 0) return { avg: "0", min: "0", max: "0" };
const avg = ( const avg = (
this.trains.reduce((acc, train) => acc + train.speed, 0) / this.trains.reduce((acc, train) => acc + train.speed, 0) /
this.trains.length this.trains.length
).toFixed(2); ).toFixed(2);
const minMax = this.trains.reduce((acc, train) => { const minMax = this.trains.reduce((acc, train) => {
if (!train.timetableData) return acc; if (!train.timetableData) return acc;
acc[0] = acc[0] =
acc[0] === undefined || train.speed < acc[0] ? train.speed : acc[0]; acc[0] === undefined || train.speed < acc[0] ? train.speed : acc[0];
acc[1] = acc[1] =
acc[1] === undefined || train.speed > acc[1] ? train.speed : acc[1]; acc[1] === undefined || train.speed > acc[1] ? train.speed : acc[1];
return acc; return acc;
}, [] as any); }, [] as any);
return { avg, min: minMax[0].toString(), max: minMax[1].toString() }; return { avg, min: minMax[0].toString(), max: minMax[1].toString() };
} }
get timetableStats(): { avg: string; min: string; max: string } { get timetableStats(): { avg: string; min: string; max: string } {
if (this.trains.length == 0) return { avg: "0", min: "0", max: "0" }; if (this.trains.length == 0) return { avg: "0", min: "0", max: "0" };
const avg = ( const avg = (
this.trains.reduce((acc, train) => train.timetableData ? acc + train.timetableData.routeDistance : acc, 0) / this.trains.reduce((acc, train) => train.timetableData ? acc + train.timetableData.routeDistance : acc, 0) /
this.trains.length this.trains.length
).toFixed(2); ).toFixed(2);
const minMax = this.trains.reduce((acc, train) => { const minMax = this.trains.reduce((acc, train) => {
if (!train.timetableData) return acc; if (!train.timetableData) return acc;
acc[0] = acc[0] =
acc[0] === undefined || train.timetableData.routeDistance < acc[0] acc[0] === undefined || train.timetableData.routeDistance < acc[0]
? train.timetableData.routeDistance ? train.timetableData.routeDistance
: acc[0]; : acc[0];
acc[1] = acc[1] =
acc[1] === undefined || train.timetableData.routeDistance > acc[1] acc[1] === undefined || train.timetableData.routeDistance > acc[1]
? train.timetableData.routeDistance ? train.timetableData.routeDistance
: acc[1]; : acc[1];
return acc; return acc;
}, [] as any); }, [] as any);
return { avg, min: minMax[0].toString(), max: minMax[1].toString() }; return { avg, min: minMax[0].toString(), max: minMax[1].toString() };
} }
get categoryList(): Map<string, number> { get categoryList(): Map<string, number> {
const map = this.trains.reduce((acc, train) => { const map = this.trains.reduce((acc, train) => {
if (!train.timetableData || !train.timetableData.category) return acc; if (!train.timetableData || !train.timetableData.category) return acc;
acc.set( acc.set(
train.timetableData.category, train.timetableData.category,
acc.get(train.timetableData.category) ? acc.get(train.timetableData.category) + 1 : 1 acc.get(train.timetableData.category) ? acc.get(train.timetableData.category) + 1 : 1
); );
return acc; return acc;
}, new Map()); }, new Map());
return new Map([...map.entries()].sort((a, b) => b[1] - a[1])); return new Map([...map.entries()].sort((a, b) => b[1] - a[1]));
} }
get locoList(): any[] { get locoList(): any[] {
const map = this.trains.reduce((acc, train) => { const map = this.trains.reduce((acc, train) => {
if (!train.timetableData || !train.locoType) return acc; if (!train.timetableData || !train.locoType) return acc;
acc.set( acc.set(
train.locoType, train.locoType,
acc.get(train.locoType) ? acc.get(train.locoType) + 1 : 1 acc.get(train.locoType) ? acc.get(train.locoType) + 1 : 1
); );
return acc; return acc;
}, new Map()); }, new Map());
const sorted = [...map.entries()] const sorted = [...map.entries()]
.sort((a, b) => b[1] - a[1]) .sort((a, b) => b[1] - a[1])
.filter((v, i) => i < 3); .filter((v, i) => i < 3);
return sorted; return sorted;
} }
get specialTrainCount(): [number, number] { get specialTrainCount(): [number, number] {
const twrList = this.trains.filter((train) => train.timetableData && train.timetableData.TWR); const twrList = this.trains.filter((train) => train.timetableData && train.timetableData.TWR);
const skrList = this.trains.filter((train) => train.timetableData && train.timetableData.SKR); const skrList = this.trains.filter((train) => train.timetableData && train.timetableData.SKR);
return [twrList.length, skrList.length]; return [twrList.length, skrList.length];
} }
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import "../../styles/responsive"; @import "../../styles/responsive";
@import "../../styles/variables"; @import "../../styles/variables";
.stats-anim { .stats-anim {
&-enter-active, &-enter-active,
&-leave-active { &-leave-active {
transition: all 150ms ease-out; transition: all 150ms ease-out;
} }
&-enter, &-enter,
&-leave-to { &-leave-to {
opacity: 0; opacity: 0;
transform: translateY(30px); transform: translateY(30px);
} }
} }
.train-stats { .train-stats {
padding: 0.3em 0; padding: 0.3em 0;
font-size: 1.1em; font-size: 1.1em;
z-index: 5; z-index: 5;
position: relative; position: relative;
} }
.stats { .stats {
&-btn { &-btn {
font-size: 1em; font-size: 1em;
padding: 0.5em; padding: 0.5em;
} }
&-header { &-header {
margin-bottom: 1rem; margin-bottom: 1rem;
} }
&-body { &-body {
position: absolute; position: absolute;
display: inline-block; display: inline-block;
max-width: 700px; max-width: 700px;
background: rgba(black, 0.85); background: rgba(black, 0.85);
border-radius: 0 1em 1em 1em; border-radius: 0 1em 1em 1em;
padding: 1rem; padding: 1rem;
} }
&-content { &-content {
font-size: 1.1em; font-size: 1.1em;
color: #ddd; color: #ddd;
} }
} }
.category, .category,
.special { .special {
&-list { &-list {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
font-size: 0.95em; font-size: 0.95em;
} }
margin-right: 0.4em; margin-right: 0.4em;
margin-bottom: 0.4em; margin-bottom: 0.4em;
&-type, &-type,
&-count { &-count {
display: inline-block; display: inline-block;
padding: 0.2em 0.4em; padding: 0.2em 0.4em;
} }
&-type { &-type {
background: rgb(88, 88, 88); background: rgb(88, 88, 88);
font-weight: 600; font-weight: 600;
} }
&-count { &-count {
background: #ffc014; background: #ffc014;
color: black; color: black;
} }
} }
.special { .special {
&-list { &-list {
font-size: 0.85em; font-size: 0.85em;
} }
&-count { &-count {
background: gray; background: gray;
color: white; color: white;
} }
&.twr > &-type { &.twr > &-type {
background-color: $twr; background-color: $twr;
color: black; color: black;
} }
&.skr > &-type { &.skr > &-type {
background-color: $skr; background-color: $skr;
color: white; color: white;
} }
} }
.warning { .warning {
display: inline-block; display: inline-block;
margin-right: 0.4em; margin-right: 0.4em;
padding: 0.2em 0.3em; padding: 0.2em 0.3em;
color: black; color: black;
font-weight: bold; font-weight: bold;
font-size: 0.85em; font-size: 0.85em;
&.twr { &.twr {
background-color: #ffc700; background-color: #ffc700;
} }
&.skr { &.skr {
background-color: #f00000; background-color: #f00000;
} }
} }
@include smallScreen { @include smallScreen {
.button { .button {
font-size: 0.85rem; font-size: 0.85rem;
} }
.stats-body { .stats-body {
display: block; display: block;
font-size: 0.9em; font-size: 0.9em;
width: 100%; width: 100%;
border-radius: 0 0 1em 1em; border-radius: 0 0 1em 1em;
} }
.btn-wrapper { .btn-wrapper {
display: flex; display: flex;
justify-content: center; justify-content: center;
margin-top: 1rem; margin-top: 1rem;
} }
} }
</style> </style>
+484 -484
View File
@@ -1,485 +1,485 @@
<template> <template>
<div class="train-table"> <div class="train-table">
<div class="no-trains" v-if="computedTrains.length == 0"> <div class="no-trains" v-if="computedTrains.length == 0">
Ups! Brak pociągów do wyświetlenia :/ Ups! Brak pociągów do wyświetlenia :/
</div> </div>
<ul class="train-list"> <ul class="train-list">
<li <li
class="train-row" class="train-row"
v-for="(train, i) in computedTrains" v-for="(train, i) in computedTrains"
:key="i" :key="i"
:id="train.timetableData.timetableId" :id="train.timetableData.timetableId"
> >
<span class="wrapper"> <span class="wrapper">
<span <span
class="info" class="info"
@click="changeScheduleShowState(train.timetableData.timetableId)" @click="changeScheduleShowState(train.timetableData.timetableId)"
> >
<div class="info-main"> <div class="info-main">
<div class="info-category"> <div class="info-category">
<div class="category-left"> <div class="category-left">
<span class="warning twr" v-if="train.timetableData.TWR"> <span class="warning twr" v-if="train.timetableData.TWR">
TWR TWR
</span> </span>
<span class="warning skr" v-if="train.timetableData.SKR"> <span class="warning skr" v-if="train.timetableData.SKR">
SKR SKR
</span> </span>
<span> <span>
<strong>{{ train.timetableData.category }}</strong> <strong>{{ train.timetableData.category }}</strong>
{{ train.trainNo }} | {{ train.trainNo }} |
<span style="color: gold"> <span style="color: gold">
{{ train.timetableData.routeDistance }} km {{ train.timetableData.routeDistance }} km
</span> </span>
</span> </span>
</div> </div>
<div class="category-right tooltip"> <div class="category-right tooltip">
<img <img
:src=" :src="
showedSchedule === train.timetableData.timetableId showedSchedule === train.timetableData.timetableId
? ascSVG ? ascSVG
: descSVG : descSVG
" "
alt="asc-arrow" alt="asc-arrow"
/> />
<span>SRJP</span> <span>SRJP</span>
<span class="tooltip-text"> <span class="tooltip-text">
Szczegółowy rozkład jazdy pociągu {{ train.trainNo }} Szczegółowy rozkład jazdy pociągu {{ train.trainNo }}
</span> </span>
</div> </div>
</div> </div>
<div class="info-route"> <div class="info-route">
<span class="info-route-text"> <span class="info-route-text">
<strong> <strong>
{{ train.timetableData.route.replace("|", " - ") }} {{ train.timetableData.route.replace("|", " - ") }}
</strong> </strong>
</span> </span>
</div> </div>
<div class="info-stops"> <div class="info-stops">
<span v-if="train.timetableData.followingStops.length > 2"> <span v-if="train.timetableData.followingStops.length > 2">
Przez: Przez:
<span <span
v-html=" v-html="
generateStopList(train.timetableData.followingStops) generateStopList(train.timetableData.followingStops)
" "
></span> ></span>
</span> </span>
</div> </div>
</div> </div>
<div class="info-bottom"> <div class="info-bottom">
<span <span
class="info-label user-badge" class="info-label user-badge"
:class="train.stopStatus || 'disconnected'" :class="train.stopStatus || 'disconnected'"
> >
<span <span
class="tooltip" class="tooltip"
:style="!train.online ? 'color: gray' : ''" :style="!train.online ? 'color: gray' : ''"
> >
<span v-if="train.stopStatus">{{ train.stopLabel }}</span> <span v-if="train.stopStatus">{{ train.stopLabel }}</span>
<span v-else-if="train.currentStationName"> <span v-else-if="train.currentStationName">
Pociąg na złej stacji! Pociąg na złej stacji!
</span> </span>
<span v-else>Sceneria offline!</span> <span v-else>Sceneria offline!</span>
<span class="tooltip-text" v-if="!train.online"> <span class="tooltip-text" v-if="!train.online">
Pociąg offline Pociąg offline
</span> </span>
</span> </span>
</span> </span>
</div> </div>
</span> </span>
<span class="driver"> <span class="driver">
<span class="driver-name"> <span class="driver-name">
<a <a
:href="'https://td2.info.pl/profile/?u=' + train.driverId" :href="'https://td2.info.pl/profile/?u=' + train.driverId"
target="_blank" target="_blank"
> >
{{ train.driverName }} {{ train.driverName }}
</a> </a>
</span> </span>
<span class="driver-type"> <span class="driver-type">
{{ train.locoType }} {{ train.locoType }}
</span> </span>
<span class="driver-loco"> <span class="driver-loco">
<img :src="train.locoURL" @error="onImageError" /> <img :src="train.locoURL" @error="onImageError" />
</span> </span>
</span> </span>
<span class="stats"> <span class="stats">
<div class="stats-main"> <div class="stats-main">
<span class="mass"> <span class="mass">
<img :src="massIcon" alt="icon-mass" /> <img :src="massIcon" alt="icon-mass" />
{{ train.mass / 1000 }}t {{ train.mass / 1000 }}t
</span> </span>
<span class="speed"> <span class="speed">
<img :src="speedIcon" alt="icon-speed" /> <img :src="speedIcon" alt="icon-speed" />
{{ train.speed }} km/h {{ train.speed }} km/h
</span> </span>
<span class="length"> <span class="length">
<img :src="lengthIcon" alt="icon-length" /> <img :src="lengthIcon" alt="icon-length" />
{{ train.length }}m {{ train.length }}m
</span> </span>
</div> </div>
<div class="stats-position"> <div class="stats-position">
<span class="station"> <span class="station">
<div class="stat-icon"> <div class="stat-icon">
<img :src="sceneryIcon" alt="icon-scenery" /> <img :src="sceneryIcon" alt="icon-scenery" />
</div> </div>
{{ train.currentStationName || "---" }} {{ train.currentStationName || "---" }}
</span> </span>
<span class="track"> <span class="track">
<div class="stat-icon"> <div class="stat-icon">
<img :src="routeIcon" alt="icon-scenery" /> <img :src="routeIcon" alt="icon-scenery" />
</div> </div>
{{ train.connectedTrack || "---" }} {{ train.connectedTrack || "---" }}
</span> </span>
<span class="signal"> <span class="signal">
<div class="stat-icon"> <div class="stat-icon">
<img :src="signalIcon" alt="icon-scenery" /> <img :src="signalIcon" alt="icon-scenery" />
</div> </div>
{{ train.signal || "---" }} {{ train.signal || "---" }}
</span> </span>
<span class="distance"> <span class="distance">
<div class="stat-icon"> <div class="stat-icon">
<img :src="distanceIcon" alt="icon-scenery" /> <img :src="distanceIcon" alt="icon-scenery" />
</div> </div>
{{ train.distance || "0" }}m {{ train.distance || "0" }}m
</span> </span>
</div> </div>
</span> </span>
</span> </span>
<TrainSchedule <TrainSchedule
:followingStops="train.timetableData.followingStops" :followingStops="train.timetableData.followingStops"
:currentStationName="train.currentStationName" :currentStationName="train.currentStationName"
@click="changeScheduleShowState(train.timetableData.timetableId)" @click="changeScheduleShowState(train.timetableData.timetableId)"
v-if="showedSchedule == train.timetableData.timetableId" v-if="showedSchedule == train.timetableData.timetableId"
/> />
</li> </li>
</ul> </ul>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { Vue, Component, Prop, Watch } from "vue-property-decorator"; import { Vue, Component, Prop, Watch } from "vue-property-decorator";
const unknownTrainImage = require("@/assets/unknown.png"); const unknownTrainImage = require("@/assets/unknown.png");
const ascSVG = require("@/assets/icon-arrow-asc.svg"); const ascSVG = require("@/assets/icon-arrow-asc.svg");
const descSVG = require("@/assets/icon-arrow-desc.svg"); const descSVG = require("@/assets/icon-arrow-desc.svg");
import Train from "@/scripts/interfaces/Train"; import Train from "@/scripts/interfaces/Train";
import Station from "@/scripts/interfaces/Station"; import Station from "@/scripts/interfaces/Station";
import TrainSchedule from "@/components/TrainsView/TrainSchedule.vue"; import TrainSchedule from "@/components/TrainsView/TrainSchedule.vue";
import TrainStop from '@/scripts/interfaces/TrainStop'; import TrainStop from '@/scripts/interfaces/TrainStop';
@Component({ @Component({
components: { TrainSchedule } components: { TrainSchedule }
}) })
export default class TrainTable extends Vue { export default class TrainTable extends Vue {
@Prop() computedTrains!: Train[]; @Prop() computedTrains!: Train[];
showedSchedule = 0; showedSchedule = 0;
ascSVG = ascSVG; ascSVG = ascSVG;
descSVG = descSVG; descSVG = descSVG;
speedIcon: string = require("@/assets/icon-speed.svg"); speedIcon: string = require("@/assets/icon-speed.svg");
massIcon: string = require("@/assets/icon-mass.svg"); massIcon: string = require("@/assets/icon-mass.svg");
lengthIcon: string = require("@/assets/icon-length.svg"); lengthIcon: string = require("@/assets/icon-length.svg");
distanceIcon: string = require("@/assets/icon-distance.svg"); distanceIcon: string = require("@/assets/icon-distance.svg");
sceneryIcon: string = require("@/assets/icon-scenery.svg"); sceneryIcon: string = require("@/assets/icon-scenery.svg");
signalIcon: string = require("@/assets/icon-signal.svg"); signalIcon: string = require("@/assets/icon-signal.svg");
routeIcon: string = require("@/assets/icon-route.svg"); routeIcon: string = require("@/assets/icon-route.svg");
changeScheduleShowState(timetableId: number) { changeScheduleShowState(timetableId: number) {
this.showedSchedule = this.showedSchedule === timetableId ? 0 : timetableId; this.showedSchedule = this.showedSchedule === timetableId ? 0 : timetableId;
} }
onImageError(e: Event) { onImageError(e: Event) {
(e.target as HTMLImageElement).src = unknownTrainImage; (e.target as HTMLImageElement).src = unknownTrainImage;
} }
generateStopList(stops: any): string | undefined { generateStopList(stops: any): string | undefined {
if (!stops) return ""; if (!stops) return "";
return stops.reduce((acc, stop: TrainStop, i) => { return stops.reduce((acc, stop: TrainStop, i) => {
if (stop.stopType.includes("ph")) acc.push(`<strong style='color:${stop.confirmed ? "springgreen" : "white"}'>${stop.stopName}</strong>`); if (stop.stopType.includes("ph")) acc.push(`<strong style='color:${stop.confirmed ? "springgreen" : "white"}'>${stop.stopName}</strong>`);
else if (i > 0 && i < stops.length - 1 && !stop.stopNameRAW.includes("po.")) else if (i > 0 && i < stops.length - 1 && !stop.stopNameRAW.includes("po."))
acc.push(`<span style='color:${stop.confirmed ? "springgreen" : "lightgray"}'>${stop.stopName}</span>`); acc.push(`<span style='color:${stop.confirmed ? "springgreen" : "lightgray"}'>${stop.stopName}</span>`);
return acc; return acc;
}, []).join(" * "); }, []).join(" * ");
} }
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import "../../styles/responsive.scss"; @import "../../styles/responsive.scss";
@import "../../styles/variables.scss"; @import "../../styles/variables.scss";
@import "../../styles/user_badge.scss"; @import "../../styles/user_badge.scss";
.no-trains { .no-trains {
text-align: center; text-align: center;
font-size: 1.5em; font-size: 1.5em;
padding: 1rem; padding: 1rem;
margin: 1rem 0; margin: 1rem 0;
background: #333; background: #333;
} }
.train { .train {
&-list { &-list {
@include smallScreen() { @include smallScreen() {
width: 100%; width: 100%;
overflow: hidden; overflow: hidden;
} }
} }
&-row { &-row {
padding: 1rem; padding: 1rem;
margin-bottom: 1rem; margin-bottom: 1rem;
background-color: $primaryCol; background-color: $primaryCol;
cursor: pointer; cursor: pointer;
& > .wrapper { & > .wrapper {
display: grid; display: grid;
grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); grid-template-columns: repeat(auto-fit, minmax(0, 1fr));
font-size: calc(0.4rem + 0.5vw); font-size: calc(0.4rem + 0.5vw);
@include smallScreen() { @include smallScreen() {
grid-template-columns: 1fr; grid-template-columns: 1fr;
grid-template-rows: repeat(3, minmax(100px, 1fr)); grid-template-rows: repeat(3, minmax(100px, 1fr));
font-size: 0.8rem; font-size: 0.8rem;
gap: 0.4em 0; gap: 0.4em 0;
} }
@include bigScreen() { @include bigScreen() {
font-size: 1.1rem; font-size: 1.1rem;
} }
} }
} }
} }
.info { .info {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: space-between; justify-content: space-between;
&-category { &-category {
font-size: 1.05em; font-size: 1.05em;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
div { div {
display: flex; display: flex;
} }
.category-right { .category-right {
padding: 0.15em 0.5em; padding: 0.15em 0.5em;
background: #1085b3; background: #1085b3;
border-radius: 1em; border-radius: 1em;
font-size: 0.9em; font-size: 0.9em;
-moz-user-select: none; -moz-user-select: none;
-webkit-user-select: none; -webkit-user-select: none;
img { img {
vertical-align: middle; vertical-align: middle;
width: 1.2em; width: 1.2em;
} }
.tooltip-text { .tooltip-text {
font-size: 0.9em; font-size: 0.9em;
background-color: #1085b3; background-color: #1085b3;
&::after { &::after {
border-color: #1085b3 transparent transparent transparent; border-color: #1085b3 transparent transparent transparent;
} }
} }
} }
} }
&-route { &-route {
display: flex; display: flex;
align-items: center; align-items: center;
font-size: 1.25em; font-size: 1.25em;
margin: 5px 0; margin: 5px 0;
} }
&-stops { &-stops {
margin-bottom: 10px; margin-bottom: 10px;
font-size: 0.75em; font-size: 0.75em;
} }
&-online { &-online {
background-color: #ce0000; background-color: #ce0000;
padding: 0.2em 0.7em; padding: 0.2em 0.7em;
font-size: 0.85em; font-size: 0.85em;
border-radius: 1em; border-radius: 1em;
&.online { &.online {
background-color: #009700; background-color: #009700;
} }
} }
&-bottom { &-bottom {
display: flex; display: flex;
align-items: center; align-items: center;
button { button {
margin-left: 10px; margin-left: 10px;
border-radius: 0.7em; border-radius: 0.7em;
padding: 0.2em 0.5em; padding: 0.2em 0.5em;
font-size: 0.85em; font-size: 0.85em;
border: 1px solid white; border: 1px solid white;
} }
} }
} }
.driver { .driver {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
flex-wrap: wrap; flex-wrap: wrap;
&-exp { &-exp {
font-size: 1.4em; font-size: 1.4em;
padding: 0.3em 0.6em; padding: 0.3em 0.6em;
border-radius: 0.4em; border-radius: 0.4em;
background-color: red; background-color: red;
} }
&-name { &-name {
margin: 0 0.3em; margin: 0 0.3em;
font-weight: bold; font-weight: bold;
} }
&-type { &-type {
color: #bbb; color: #bbb;
margin-left: 1em; margin-left: 1em;
} }
&-loco { &-loco {
width: 100%; width: 100%;
text-align: center; text-align: center;
} }
&-loco img { &-loco img {
width: 13em; width: 13em;
max-width: 190px; max-width: 190px;
} }
} }
.stats { .stats {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: space-around; justify-content: space-around;
&-main { &-main {
display: flex; display: flex;
margin-bottom: 1.5em; margin-bottom: 1.5em;
& > span { & > span {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
width: 100%; width: 100%;
} }
img { img {
margin: 0 0.3em; margin: 0 0.3em;
width: 2em; width: 2em;
} }
} }
&-position { &-position {
display: flex; display: flex;
margin-top: 1em; margin-top: 1em;
text-align: center; text-align: center;
font-size: 0.9em; font-size: 0.9em;
p { p {
color: #00cff3; color: #00cff3;
} }
& > span { & > span {
width: 100%; width: 100%;
img { img {
width: 2em; width: 2em;
} }
} }
} }
} }
.warning { .warning {
border-radius: 15px; border-radius: 15px;
padding: 0.1em 1.2em; padding: 0.1em 1.2em;
margin-right: 0.5em; margin-right: 0.5em;
display: flex; display: flex;
align-items: center; align-items: center;
color: white; color: white;
font-weight: bold; font-weight: bold;
font-size: 0.7em; font-size: 0.7em;
&.twr { &.twr {
border: 2px solid $twr; border: 2px solid $twr;
} }
&.skr { &.skr {
border: 2px solid $skr; border: 2px solid $skr;
} }
} }
@include bigScreen() { @include bigScreen() {
.item { .item {
font-size: 1rem; font-size: 1rem;
} }
} }
@include smallScreen() { @include smallScreen() {
.info-bottom { .info-bottom {
text-align: center; text-align: center;
} }
} }
</style> </style>
+160 -160
View File
@@ -1,161 +1,161 @@
{ {
"options": [{ "options": [{
"id": "is-default", "id": "is-default",
"name": "default", "name": "default",
"section": "access", "section": "access",
"value": true, "value": true,
"defaultValue": true, "defaultValue": true,
"content": "W PACZCE" "content": "W PACZCE"
}, },
{ {
"id": "not-default", "id": "not-default",
"name": "notDefault", "name": "notDefault",
"section": "access", "section": "access",
"value": true, "value": true,
"defaultValue": true, "defaultValue": true,
"content": "POZA PACZKĄ" "content": "POZA PACZKĄ"
}, },
{ {
"id": "non-public", "id": "non-public",
"name": "nonPublic", "name": "nonPublic",
"section": "access", "section": "access",
"value": true, "value": true,
"defaultValue": true, "defaultValue": true,
"content": "NIEPUBLICZNA" "content": "NIEPUBLICZNA"
}, },
{ {
"id": "SPK", "id": "SPK",
"name": "SPK", "name": "SPK",
"section": "control", "section": "control",
"value": true, "value": true,
"defaultValue": true, "defaultValue": true,
"content": "SPK" "content": "SPK"
}, },
{ {
"id": "SCS", "id": "SCS",
"name": "SCS", "name": "SCS",
"section": "control", "section": "control",
"value": true, "value": true,
"defaultValue": true, "defaultValue": true,
"content": "SCS" "content": "SCS"
}, },
{ {
"id": "by-hand", "id": "by-hand",
"name": "ręczne", "name": "ręczne",
"section": "control", "section": "control",
"value": true, "value": true,
"defaultValue": true, "defaultValue": true,
"content": "RĘCZNE" "content": "RĘCZNE"
}, },
{ {
"id": "levers", "id": "levers",
"name": "mechaniczne", "name": "mechaniczne",
"section": "control", "section": "control",
"value": true, "value": true,
"defaultValue": true, "defaultValue": true,
"content": "MECHANICZNE" "content": "MECHANICZNE"
}, },
{ {
"id": "modern", "id": "modern",
"name": "współczesna", "name": "współczesna",
"section": "signals", "section": "signals",
"value": true, "value": true,
"defaultValue": true, "defaultValue": true,
"content": "WSPÓŁCZESNA" "content": "WSPÓŁCZESNA"
}, },
{ {
"id": "semaphore", "id": "semaphore",
"name": "kształtowa", "name": "kształtowa",
"section": "signals", "section": "signals",
"value": true, "value": true,
"defaultValue": true, "defaultValue": true,
"content": "KSZTAŁTOWA" "content": "KSZTAŁTOWA"
}, },
{ {
"id": "mixed", "id": "mixed",
"name": "mieszana", "name": "mieszana",
"section": "signals", "section": "signals",
"value": true, "value": true,
"defaultValue": true, "defaultValue": true,
"content": "MIESZANA" "content": "MIESZANA"
}, },
{ {
"id": "historic", "id": "historic",
"name": "historyczna", "name": "historyczna",
"section": "signals", "section": "signals",
"value": true, "value": true,
"defaultValue": true, "defaultValue": true,
"content": "HISTORYCZNA" "content": "HISTORYCZNA"
}, },
{ {
"id": "free", "id": "free",
"name": "free", "name": "free",
"section": "status", "section": "status",
"value": false, "value": false,
"defaultValue": false, "defaultValue": false,
"content": "WOLNA" "content": "WOLNA"
}, },
{ {
"id": "occupied", "id": "occupied",
"name": "occupied", "name": "occupied",
"section": "status", "section": "status",
"value": true, "value": true,
"defaultValue": true, "defaultValue": true,
"content": "ZAJĘTA" "content": "ZAJĘTA"
}, },
{ {
"id": "ending", "id": "ending",
"name": "ending", "name": "ending",
"section": "status", "section": "status",
"value": true, "value": true,
"defaultValue": true, "defaultValue": true,
"content": "KOŃCZY" "content": "KOŃCZY"
} }
], ],
"sliders": [{ "sliders": [{
"id": "min-level", "id": "min-level",
"name": "minLevel", "name": "minLevel",
"minRange": 0, "minRange": 0,
"maxRange": 20, "maxRange": 20,
"value": 0, "value": 0,
"defaultValue": 0, "defaultValue": 0,
"content": "MINIMALNY WYMAGANY POZIOM DYŻURNEGO" "content": "MINIMALNY WYMAGANY POZIOM DYŻURNEGO"
}, },
{ {
"id": "min-oneway-e", "id": "min-oneway-e",
"name": "minOneWayCatenary", "name": "minOneWayCatenary",
"minRange": 0, "minRange": 0,
"maxRange": 5, "maxRange": 5,
"value": 0, "value": 0,
"defaultValue": 0, "defaultValue": 0,
"content": "SZLAKI JEDNOTOROWE ZELEKTR. (MINIMUM)" "content": "SZLAKI JEDNOTOROWE ZELEKTR. (MINIMUM)"
}, },
{ {
"id": "min-oneway-ne", "id": "min-oneway-ne",
"name": "minOneWay", "name": "minOneWay",
"minRange": 0, "minRange": 0,
"maxRange": 5, "maxRange": 5,
"value": 0, "value": 0,
"defaultValue": 0, "defaultValue": 0,
"content": "SZLAKI JEDNOTOROWE NIEZELEKTR. (MINIMUM)" "content": "SZLAKI JEDNOTOROWE NIEZELEKTR. (MINIMUM)"
}, },
{ {
"id": "min-twoway-e", "id": "min-twoway-e",
"name": "minTwoWayCatenary", "name": "minTwoWayCatenary",
"minRange": 0, "minRange": 0,
"maxRange": 5, "maxRange": 5,
"value": 0, "value": 0,
"defaultValue": 0, "defaultValue": 0,
"content": "SZLAKI DWUTOROWE ZELEKTR. (MINIMUM)" "content": "SZLAKI DWUTOROWE ZELEKTR. (MINIMUM)"
}, },
{ {
"id": "min-twoway-ne", "id": "min-twoway-ne",
"name": "minTwoWay", "name": "minTwoWay",
"minRange": 0, "minRange": 0,
"maxRange": 5, "maxRange": 5,
"value": 0, "value": 0,
"defaultValue": 0, "defaultValue": 0,
"content": "SZLAKI DWUTOROWE NIEZELEKTR. (MINIMUM)" "content": "SZLAKI DWUTOROWE NIEZELEKTR. (MINIMUM)"
} }
] ]
} }
File diff suppressed because one or more lines are too long
+18 -18
View File
@@ -1,19 +1,19 @@
import TrainStop from "./TrainStop"; import TrainStop from "./TrainStop";
export default interface ScheduledTrain { export default interface ScheduledTrain {
trainNo: number; trainNo: number;
driverName: string; driverName: string;
driverId: number; driverId: number;
currentStationName: string; currentStationName: string;
currentStationHash: string; currentStationHash: string;
category: string; category: string;
stopInfo: TrainStop; stopInfo: TrainStop;
terminatesAt: string; terminatesAt: string;
beginsAt: string; beginsAt: string;
nearestStop: string; nearestStop: string;
stopLabel: string; stopLabel: string;
stopStatus: string; stopStatus: string;
stopStatusID: number; stopStatusID: number;
} }
+16 -16
View File
@@ -1,16 +1,16 @@
export default interface Timetable { export default interface Timetable {
trainNo: number; trainNo: number;
driverName: string; driverName: string;
category: string; category: string;
stopName: string; stopName: string;
stopType: string; stopType: string;
arrivalTime: number; arrivalTime: number;
arrivalDelay: number; arrivalDelay: number;
departureTime: number; departureTime: number;
departureDelay: number; departureDelay: number;
confirmed: boolean; confirmed: boolean;
stopped: boolean; stopped: boolean;
stopTime: number; stopTime: number;
beginsHere: boolean; beginsHere: boolean;
terminatesHere: boolean; terminatesHere: boolean;
} }
+31 -31
View File
@@ -1,31 +1,31 @@
import TrainStop from '@/scripts/interfaces/TrainStop'; import TrainStop from '@/scripts/interfaces/TrainStop';
export default interface Train { export default interface Train {
mass: number; mass: number;
length: number; length: number;
speed: number; speed: number;
signal: string; signal: string;
distance: number; distance: number;
connectedTrack: string; connectedTrack: string;
driverId: number; driverId: number;
trainNo: number; trainNo: number;
driverName: string; driverName: string;
currentStationName: string; currentStationName: string;
currentStationHash: string; currentStationHash: string;
locoURL: string; locoURL: string;
locoType: string; locoType: string;
online: boolean; online: boolean;
timetableData?: { timetableData?: {
timetableId: number; timetableId: number;
category: string; category: string;
route: string; route: string;
followingStops: TrainStop[]; followingStops: TrainStop[];
TWR: boolean; TWR: boolean;
SKR: boolean; SKR: boolean;
routeDistance: number; routeDistance: number;
}; };
stopStatus: string; stopStatus: string;
stopLabel: string; stopLabel: string;
} }
+28 -28
View File
@@ -1,28 +1,28 @@
export default interface TrainStop { export default interface TrainStop {
stopName: string; stopName: string;
stopNameRAW: string; stopNameRAW: string;
stopType: string; stopType: string;
mainStop: boolean; mainStop: boolean;
arrivalLine: string; arrivalLine: string;
arrivalTimeString: string; arrivalTimeString: string;
arrivalTimestamp: number; arrivalTimestamp: number;
arrivalRealTimeString: string; arrivalRealTimeString: string;
arrivalRealTimestamp: number; arrivalRealTimestamp: number;
arrivalDelay: number; arrivalDelay: number;
departureLine: string; departureLine: string;
departureTimeString: string; departureTimeString: string;
departureTimestamp: number; departureTimestamp: number;
departureRealTimeString: string; departureRealTimeString: string;
departureRealTimestamp: number; departureRealTimestamp: number;
departureDelay: number; departureDelay: number;
comments?: any; comments?: any;
beginsHere: boolean; beginsHere: boolean;
terminatesHere: boolean; terminatesHere: boolean;
confirmed: boolean; confirmed: boolean;
stopped: boolean; stopped: boolean;
stopTime: number; stopTime: number;
} }
+138 -140
View File
@@ -1,140 +1,138 @@
import Station from '@/scripts/interfaces/Station'; import Station from '@/scripts/interfaces/Station';
export default class StationFilterManager { export default class StationFilterManager {
private filterInitStates = { private filterInitStates = {
default: false, default: false,
notDefault: false, notDefault: false,
nonPublic: false, nonPublic: false,
SPK: false, SPK: false,
SCS: false, SCS: false,
ręczne: false, ręczne: false,
mechaniczne: false, mechaniczne: false,
współczesna: false, współczesna: false,
kształtowa: false, kształtowa: false,
historyczna: false, historyczna: false,
mieszana: false, mieszana: false,
minLevel: 0, minLevel: 0,
minOneWayCatenary: 0, minOneWayCatenary: 0,
minOneWay: 0, minOneWay: 0,
minTwoWayCatenary: 0, minTwoWayCatenary: 0,
minTwoWay: 0, minTwoWay: 0,
'no-1track': false, 'no-1track': false,
'no-2track': false, 'no-2track': false,
free: true, free: true,
occupied: false, occupied: false,
ending: false, ending: false,
}; };
private filters = { ...this.filterInitStates }; private filters = { ...this.filterInitStates };
private sorter: { index: number; dir: number } = { index: 0, dir: 1 }; private sorter: { index: number; dir: number } = { index: 0, dir: 1 };
filteredStationList(stationList: Station[]): Station[] { filteredStationList(stationList: Station[]): Station[] {
return stationList return stationList
.filter(station => { .filter(station => {
if (!station.reqLevel || station.reqLevel == '-1') return true; if ((station.nonPublic || !station.reqLevel) && this.filters['nonPublic']) return false;
if ((station.nonPublic || !station.reqLevel) && this.filters['nonPublic']) return false; if (station.online && station.occupiedTo == 'KOŃCZY' && this.filters['ending']) return false;
if (station.online && station.occupiedTo == 'KOŃCZY' && this.filters['ending']) return false; if (station.online && this.filters['occupied']) return false;
if (!station.online && this.filters['free']) return false;
if (station.online && this.filters['occupied']) return false;
if (!station.online && this.filters['free']) return false; if (station.default && this.filters['default']) return false;
if (!station.default && this.filters['notDefault']) return false;
if (station.default && this.filters['default']) return false;
if (!station.default && this.filters['notDefault']) return false; if (parseInt(station.reqLevel) < this.filters['minLevel']) return false;
if (parseInt(station.reqLevel) < this.filters['minLevel']) return false; if (this.filters['no-1track'] && (station.routes.oneWay.catenary != 0 || station.routes.oneWay.noCatenary != 0)) return false;
if (this.filters['no-2track'] && (station.routes.twoWay.catenary != 0 || station.routes.twoWay.noCatenary != 0)) return false;
if (this.filters['no-1track'] && (station.routes.oneWay.catenary != 0 || station.routes.oneWay.noCatenary != 0)) return false;
if (this.filters['no-2track'] && (station.routes.twoWay.catenary != 0 || station.routes.twoWay.noCatenary != 0)) return false; if (station.routes.oneWay.catenary < this.filters['minOneWayCatenary']) return false;
if (station.routes.oneWay.noCatenary < this.filters['minOneWay']) return false;
if (station.routes.oneWay.catenary < this.filters['minOneWayCatenary']) return false;
if (station.routes.oneWay.noCatenary < this.filters['minOneWay']) return false; if (station.routes.twoWay.catenary < this.filters['minTwoWayCatenary']) return false;
if (station.routes.twoWay.noCatenary < this.filters['minTwoWay']) return false;
if (station.routes.twoWay.catenary < this.filters['minTwoWayCatenary']) return false;
if (station.routes.twoWay.noCatenary < this.filters['minTwoWay']) return false; if (this.filters[station.controlType]) return false;
if (this.filters[station.signalType]) return false;
if (this.filters[station.controlType]) return false;
if (this.filters[station.signalType]) return false; if (this.filters['SPK'] && (station.controlType === 'SPK' || station.controlType.includes('+SPK'))) return false;
if (this.filters['SCS'] && (station.controlType === 'SCS' || station.controlType.includes('+SCS'))) return false;
if (this.filters['SPK'] && (station.controlType === 'SPK' || station.controlType.includes('+SPK'))) return false;
if (this.filters['SCS'] && (station.controlType === 'SCS' || station.controlType.includes('+SCS'))) return false; if (this.filters['SCS'] && this.filters['SPK'] && (station.controlType.includes('SPK') || station.controlType.includes('SCS'))) return false;
if (this.filters['SCS'] && this.filters['SPK'] && (station.controlType.includes('SPK') || station.controlType.includes('SCS'))) return false; if (this.filters['mechaniczne'] && station.controlType.includes('mechaniczne')) return false;
if (this.filters['mechaniczne'] && station.controlType.includes('mechaniczne')) return false; if (this.filters['czne'] && station.controlType.includes('czne')) return false;
if (this.filters['ręczne'] && station.controlType.includes('ręczne')) return false; return true;
})
return true; .sort((a, b) => {
}) switch (this.sorter.index) {
.sort((a, b) => { case 1:
switch (this.sorter.index) { if (parseInt(a.reqLevel) > parseInt(b.reqLevel)) return this.sorter.dir;
case 1: if (parseInt(a.reqLevel) < parseInt(b.reqLevel)) return -this.sorter.dir;
if (parseInt(a.reqLevel) > parseInt(b.reqLevel)) return this.sorter.dir; break;
if (parseInt(a.reqLevel) < parseInt(b.reqLevel)) return -this.sorter.dir;
break; case 2:
if (a.statusTimestamp > b.statusTimestamp) return this.sorter.dir;
case 2: if (a.statusTimestamp < b.statusTimestamp) return -this.sorter.dir;
if (a.statusTimestamp > b.statusTimestamp) return this.sorter.dir; break;
if (a.statusTimestamp < b.statusTimestamp) return -this.sorter.dir;
break; case 3:
if (a.dispatcherName.toLowerCase() > b.dispatcherName.toLowerCase()) return this.sorter.dir;
case 3: if (a.dispatcherName.toLowerCase() < b.dispatcherName.toLowerCase()) return -this.sorter.dir;
if (a.dispatcherName.toLowerCase() > b.dispatcherName.toLowerCase()) return this.sorter.dir; break;
if (a.dispatcherName.toLowerCase() < b.dispatcherName.toLowerCase()) return -this.sorter.dir;
break; case 4:
if (a.dispatcherExp > b.dispatcherExp) return this.sorter.dir;
case 4: if (a.dispatcherExp < b.dispatcherExp) return -this.sorter.dir;
if (a.dispatcherExp > b.dispatcherExp) return this.sorter.dir; break;
if (a.dispatcherExp < b.dispatcherExp) return -this.sorter.dir;
break; case 7:
if (a.currentUsers > b.currentUsers) return this.sorter.dir;
case 7: if (a.currentUsers < b.currentUsers) return -this.sorter.dir;
if (a.currentUsers > b.currentUsers) return this.sorter.dir; if (a.maxUsers > b.maxUsers) return this.sorter.dir;
if (a.currentUsers < b.currentUsers) return -this.sorter.dir; if (a.maxUsers < b.maxUsers) return -this.sorter.dir;
if (a.maxUsers > b.maxUsers) return this.sorter.dir; break;
if (a.maxUsers < b.maxUsers) return -this.sorter.dir;
break; case 8:
if (a.spawns > b.spawns) return this.sorter.dir;
case 8: if (a.spawns < b.spawns) return -this.sorter.dir;
if (a.spawns > b.spawns) return this.sorter.dir;
if (a.spawns < b.spawns) return -this.sorter.dir; break;
break; case 9:
if (a.scheduledTrains.length > b.scheduledTrains.length) return this.sorter.dir;
case 9: if (a.scheduledTrains.length < b.scheduledTrains.length) return -this.sorter.dir;
if (a.scheduledTrains.length > b.scheduledTrains.length) return this.sorter.dir;
if (a.scheduledTrains.length < b.scheduledTrains.length) return -this.sorter.dir; default:
break;
default: }
break;
} if (a.stationName.toLowerCase() >= b.stationName.toLowerCase()) return this.sorter.dir;
return -this.sorter.dir;
if (a.stationName.toLowerCase() >= b.stationName.toLowerCase()) return this.sorter.dir; });
return -this.sorter.dir; }
});
} changeFilterValue(filter: { name: string; value: number }) {
this.filters[filter.name] = filter.value;
changeFilterValue(filter: { name: string; value: number }) { }
this.filters[filter.name] = filter.value;
} resetFilters() {
this.filters = { ...this.filterInitStates };
resetFilters() { }
this.filters = { ...this.filterInitStates };
} changeSorter(index: number) {
if (index > 4 && index < 7) return;
changeSorter(index: number) {
if (index > 4 && index < 7) return; if (index == this.sorter.index) this.sorter.dir = -1 * this.sorter.dir;
else this.sorter.dir = 1;
if (index == this.sorter.index) this.sorter.dir = -1 * this.sorter.dir;
else this.sorter.dir = 1; this.sorter.index = index;
}
this.sorter.index = index;
} getSorter() {
return this.sorter;
getSorter() { }
return this.sorter; }
}
}
+42 -42
View File
@@ -1,42 +1,42 @@
export default class StorageManager { export default class StorageManager {
static registerStorage(name: string) { static registerStorage(name: string) {
window.localStorage.setItem(name, '1'); window.localStorage.setItem(name, '1');
} }
static unregisterStorage(name: string) { static unregisterStorage(name: string) {
window.localStorage.removeItem(name); window.localStorage.removeItem(name);
} }
static isRegistered(name: string) { static isRegistered(name: string) {
return window.localStorage.getItem(name) ? true : false; return window.localStorage.getItem(name) ? true : false;
} }
static setBooleanValue(key: string, val: boolean) { static setBooleanValue(key: string, val: boolean) {
window.localStorage.setItem(key, val.toString()); window.localStorage.setItem(key, val.toString());
} }
static setNumericValue(key: string, val: number) { static setNumericValue(key: string, val: number) {
window.localStorage.setItem(key, val.toString()); window.localStorage.setItem(key, val.toString());
} }
static setStringValue(key: string, val: string) { static setStringValue(key: string, val: string) {
window.localStorage.setItem(key, val); window.localStorage.setItem(key, val);
} }
static removeValue(key: string) { static removeValue(key: string) {
window.localStorage.removeItem(key); window.localStorage.removeItem(key);
} }
static getBooleanValue(key: string): boolean { static getBooleanValue(key: string): boolean {
return window.localStorage.getItem(key) === 'true' ? true : false; return window.localStorage.getItem(key) === 'true' ? true : false;
} }
static getStringValue(key: string): string { static getStringValue(key: string): string {
return window.localStorage.getItem(key) || ''; return window.localStorage.getItem(key) || '';
} }
static getNumericValue(key: string): number { static getNumericValue(key: string): number {
const itemValue = window.localStorage.getItem(key); const itemValue = window.localStorage.getItem(key);
return itemValue ? parseInt(itemValue) : 0; return itemValue ? parseInt(itemValue) : 0;
} }
} }
+625 -625
View File
File diff suppressed because it is too large Load Diff
+232 -232
View File
@@ -1,233 +1,233 @@
:root { :root {
font-size: 16px; font-size: 16px;
} }
::-webkit-scrollbar { ::-webkit-scrollbar {
width: 0.5rem; width: 0.5rem;
height: 0.5rem; height: 0.5rem;
&-track { &-track {
background: #222; background: #222;
} }
&-thumb { &-thumb {
border-radius: 1rem; border-radius: 1rem;
background: #777; background: #777;
} }
} }
.tooltip { .tooltip {
position: relative; position: relative;
& > &-text { & > &-text {
display: inline-block; display: inline-block;
width: 150px; width: 150px;
padding: .5rem .35rem; padding: .5rem .35rem;
background-color: #830000; background-color: #830000;
border-radius: .5rem; border-radius: .5rem;
display: inline-block; display: inline-block;
max-width: 150px; max-width: 150px;
font-size: 1em; font-size: 1em;
text-align: center; text-align: center;
color: #fff; color: #fff;
position: absolute; position: absolute;
z-index: 1; z-index: 1;
visibility: hidden; visibility: hidden;
opacity: 0; opacity: 0;
left: 50%; left: 50%;
transform: translate(-50%, calc(-100% - 1rem)); transform: translate(-50%, calc(-100% - 1rem));
transition: opacity 0.3s; transition: opacity 0.3s;
&::after { &::after {
content: ""; content: "";
position: absolute; position: absolute;
top: 100%; top: 100%;
left: 50%; left: 50%;
margin-left: -5px; margin-left: -5px;
border-width: 5px; border-width: 5px;
border-style: solid; border-style: solid;
border-color: #830000 transparent transparent transparent; border-color: #830000 transparent transparent transparent;
} }
} }
&:hover > &-text { &:hover > &-text {
@include smallScreen() { @include smallScreen() {
display: none; display: none;
} }
visibility: visible; visibility: visible;
opacity: 1; opacity: 1;
} }
} }
html { html {
scroll-behavior: smooth; scroll-behavior: smooth;
} }
body { body {
width: 100%; width: 100%;
margin: 0; margin: 0;
// font-family: "Open Sans", sans-serif; // font-family: "Open Sans", sans-serif;
font-family: "Quicksand", sans-serif; font-family: "Quicksand", sans-serif;
overflow-x: hidden; overflow-x: hidden;
} }
button, button,
input, input,
select { select {
// font-family: "Open Sans", sans-serif; // font-family: "Open Sans", sans-serif;
font-family: "Quicksand", sans-serif; font-family: "Quicksand", sans-serif;
} }
input { input {
border: 1px solid white; border: 1px solid white;
background: none; background: none;
color: white; color: white;
font-size: 1em; font-size: 1em;
padding: 0.15em; padding: 0.15em;
margin: 0.2em; margin: 0.2em;
max-width: 55px; max-width: 55px;
outline: none; outline: none;
&::placeholder { &::placeholder {
color: #bebebe; color: #bebebe;
} }
} }
*, *,
*::before, *::before,
*::after { *::after {
box-sizing: border-box; box-sizing: border-box;
padding: 0; padding: 0;
margin: 0; margin: 0;
-webkit-tap-highlight-color: transparent; -webkit-tap-highlight-color: transparent;
} }
.default-station { .default-station {
font-weight: bold; font-weight: bold;
color: $accentCol; color: $accentCol;
} }
.card { .card {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
position: fixed; position: fixed;
top: 50%; top: 50%;
left: 50%; left: 50%;
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
z-index: 4; z-index: 4;
overflow: auto; overflow: auto;
background: #262a2e; background: #262a2e;
box-shadow: 0 0 15px 5px #474747; box-shadow: 0 0 15px 5px #474747;
width: 75%; width: 75%;
max-width: 750px; max-width: 750px;
max-height: 95%; max-height: 95%;
// font-size: calc(0.6rem + 0.5vw); // font-size: calc(0.6rem + 0.5vw);
font-size: calc(0.45rem + 0.35vw); font-size: calc(0.45rem + 0.35vw);
@include smallScreen { @include smallScreen {
width: 95%; width: 95%;
} }
@include bigScreen { @include bigScreen {
font-size: 1.4rem; font-size: 1.4rem;
} }
&-exit { &-exit {
position: absolute; position: absolute;
top: 0; top: 0;
right: 0; right: 0;
margin: 0.3em 0em; margin: 0.3em 0em;
img { img {
width: 1.6em; width: 1.6em;
} }
cursor: pointer; cursor: pointer;
} }
} }
.title { .title {
color: $accentCol; color: $accentCol;
font-weight: 600; font-weight: 600;
padding: .35em 0; padding: .35em 0;
} }
.button { .button {
display: flex; display: flex;
align-items: center; align-items: center;
background: #333; background: #333;
border: none; border: none;
color: #e0e0e0; color: #e0e0e0;
font-size: 0.9em; font-size: 0.9em;
outline: none; outline: none;
padding: 0.35em; padding: 0.35em;
cursor: pointer; cursor: pointer;
transition: all 0.3s; transition: all 0.3s;
&.open { &.open {
color: $accentCol; color: $accentCol;
border: none; border: none;
} }
&:hover { &:hover {
background: rgba(#e0e0e0, 0.4); background: rgba(#e0e0e0, 0.4);
} }
} }
a { a {
color: white; color: white;
text-decoration: none; text-decoration: none;
transition: color 0.3s; transition: color 0.3s;
&:hover, &:hover,
&:focus { &:focus {
color: $accentCol; color: $accentCol;
border: none; border: none;
outline: none; outline: none;
} }
} }
ul { ul {
padding: 0; padding: 0;
list-style: none; list-style: none;
} }
.flex { .flex {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
width: 100%; width: 100%;
&-spaced { &-spaced {
justify-content: space-between; justify-content: space-between;
} }
&-column { &-column {
flex-direction: column; flex-direction: column;
} }
} }
+50 -50
View File
@@ -1,51 +1,51 @@
$free: #8a8a8a; $free: #8a8a8a;
$ending: #e6c300; $ending: #e6c300;
$no-limit: #0077ae; $no-limit: #0077ae;
$unav: #ff3d5d; $unav: #ff3d5d;
$brb: #e6a100; $brb: #e6a100;
$no-space: #222; $no-space: #222;
$taken: #09a116; $taken: #09a116;
.status { .status {
border-radius: 1rem; border-radius: 1rem;
font-weight: 500; font-weight: 500;
font-size: 0.9em; font-size: 0.9em;
padding: 0.2em 0.45em; padding: 0.2em 0.45em;
background-color: $taken; background-color: $taken;
&.free { &.free {
background-color: $free; background-color: $free;
font-size: 0.95em; font-size: 0.95em;
} }
&.ending { &.ending {
background-color: $ending; background-color: $ending;
color: black; color: black;
font-size: 0.9em; font-size: 0.9em;
} }
&.no-limit { &.no-limit {
background-color: $no-limit; background-color: $no-limit;
font-size: 0.85em; font-size: 0.85em;
} }
&.not-signed, &.not-signed,
&.unavailable { &.unavailable {
background-color: $unav; background-color: $unav;
font-size: 0.8em; font-size: 0.8em;
} }
&.brb { &.brb {
background-color: $brb; background-color: $brb;
color: black; color: black;
font-size: 0.95em; font-size: 0.95em;
} }
&.no-space { &.no-space {
background-color: $no-space; background-color: $no-space;
color: white; color: white;
font-size: 0.85em; font-size: 0.85em;
} }
} }
+53 -53
View File
@@ -1,54 +1,54 @@
$no-timetable: #aaa; $no-timetable: #aaa;
$departed: springgreen; $departed: springgreen;
$stopped: #ffa600; $stopped: #ffa600;
$online: gold; $online: gold;
$terminated: red; $terminated: red;
$disconnected: slategray; $disconnected: slategray;
.user-badge { .user-badge {
border: 2px solid white; border: 2px solid white;
z-index: 4; z-index: 4;
margin-top: 0.5rem; margin-top: 0.5rem;
margin-right: 0.5rem; margin-right: 0.5rem;
border-radius: 0.7em; border-radius: 0.7em;
padding: 0.3em 0.5em; padding: 0.3em 0.5em;
font-size: 0.95em; font-size: 0.95em;
&.borderless { &.borderless {
border: none; border: none;
margin: 0; margin: 0;
padding: 0; padding: 0;
} }
&.no-timetable { &.no-timetable {
border: 2px solid $no-timetable; border: 2px solid $no-timetable;
a { a {
color: $no-timetable; color: $no-timetable;
pointer-events: none; pointer-events: none;
} }
} }
&.departed { &.departed {
border: 2px solid $departed; border: 2px solid $departed;
} }
&.stopped { &.stopped {
border: 2px solid $stopped; border: 2px solid $stopped;
} }
&.online { &.online {
border: 2px solid $online; border: 2px solid $online;
} }
&.terminated { &.terminated {
border: 2px solid $terminated; border: 2px solid $terminated;
} }
&.disconnected { &.disconnected {
border: 1px solid $disconnected; border: 1px solid $disconnected;
} }
} }
+147 -147
View File
@@ -1,148 +1,148 @@
<template> <template>
<div class="scenery-view"> <div class="scenery-view">
<div <div
class="scenery-offline" class="scenery-offline"
v-if="!stationInfo && dataStatus == 2 && currentPath === '/scenery'" v-if="!stationInfo && dataStatus == 2 && currentPath === '/scenery'"
> >
Ups! Nie znaleziono danej stacji bądź jest ona offline! Ups! Nie znaleziono danej stacji bądź jest ona offline!
<button class="button"> <button class="button">
<router-link to="/">Wróć na stronę główną</router-link> <router-link to="/">Wróć na stronę główną</router-link>
</button> </button>
</div> </div>
<div class="scenery-wrapper" v-if="stationInfo"> <div class="scenery-wrapper" v-if="stationInfo">
<SceneryInfo :stationInfo="stationInfo" :timetableOnly="timetableOnly" /> <SceneryInfo :stationInfo="stationInfo" :timetableOnly="timetableOnly" />
<SceneryTimetable <SceneryTimetable
:stationInfo="stationInfo" :stationInfo="stationInfo"
:timetableOnly="timetableOnly" :timetableOnly="timetableOnly"
:dataStatus="timetableDataStatus" :dataStatus="timetableDataStatus"
/> />
</div> </div>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { Vue, Component } from "vue-property-decorator"; import { Vue, Component } from "vue-property-decorator";
import { Getter } from "vuex-class"; import { Getter } from "vuex-class";
import Station from "@/scripts/interfaces/Station"; import Station from "@/scripts/interfaces/Station";
import Train from "@/scripts/interfaces/Train"; import Train from "@/scripts/interfaces/Train";
import SceneryInfo from "@/components/SceneryView/SceneryInfo.vue"; import SceneryInfo from "@/components/SceneryView/SceneryInfo.vue";
import SceneryTimetable from "@/components/SceneryView/SceneryTimetable.vue"; import SceneryTimetable from "@/components/SceneryView/SceneryTimetable.vue";
@Component({ @Component({
components: { SceneryInfo, SceneryTimetable }, components: { SceneryInfo, SceneryTimetable },
}) })
export default class SceneryView extends Vue { export default class SceneryView extends Vue {
@Getter("getStationList") storeStationList!: Station[]; @Getter("getStationList") storeStationList!: Station[];
@Getter("getTimetableDataStatus") timetableDataStatus!: number; @Getter("getTimetableDataStatus") timetableDataStatus!: number;
@Getter("getDataStatus") dataStatus!: number; @Getter("getDataStatus") dataStatus!: number;
timetableOnly: boolean = false; timetableOnly: boolean = false;
activated() { activated() {
this.timetableOnly = this.timetableOnly =
this.$route.query["timetable_only"] == "1" ? true : false; this.$route.query["timetable_only"] == "1" ? true : false;
} }
get currentPath() { get currentPath() {
return this.$route.path; return this.$route.path;
} }
// get dataLoaded() { // get dataLoaded() {
// return this.storeStationList ? true : false; // return this.storeStationList ? true : false;
// } // }
get stationInfo(): Station | null { get stationInfo(): Station | null {
if (!this.$route.query.hash || !this.storeStationList) return null; if (!this.$route.query.hash || !this.storeStationList) return null;
const info = const info =
this.storeStationList.find( this.storeStationList.find(
(station) => station.stationHash === this.$route.query.hash.toString() (station) => station.stationHash === this.$route.query.hash.toString()
) || null; ) || null;
return info; return info;
} }
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import "../styles/responsive.scss"; @import "../styles/responsive.scss";
@import "../styles/variables.scss"; @import "../styles/variables.scss";
h3 { h3 {
margin: 0.5em 0; margin: 0.5em 0;
padding: 0.3em; padding: 0.3em;
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
font-size: 1.2em; font-size: 1.2em;
img { img {
width: 1.1em; width: 1.1em;
margin-left: 0.5em; margin-left: 0.5em;
} }
} }
.scenery { .scenery {
&-view { &-view {
min-height: 100%; min-height: 100%;
display: flex; display: flex;
justify-content: center; justify-content: center;
font-size: calc(0.5rem + 0.65vw); font-size: calc(0.5rem + 0.65vw);
@include bigScreen() { @include bigScreen() {
font-size: 1.25rem; font-size: 1.25rem;
align-items: flex-start; align-items: flex-start;
} }
@include smallScreen { @include smallScreen {
font-size: calc(0.5rem + 1vw); font-size: calc(0.5rem + 1vw);
} }
} }
&-offline { &-offline {
align-self: center; align-self: center;
font-size: 2em; font-size: 2em;
text-align: center; text-align: center;
padding: 0 1em; padding: 0 1em;
color: $warningCol; color: $warningCol;
display: inline-block; display: inline-block;
.button { .button {
margin: 1rem auto; margin: 1rem auto;
font-size: 0.85em; font-size: 0.85em;
} }
} }
&-wrapper { &-wrapper {
// background: #555; // background: #555;
max-width: 950px; max-width: 950px;
width: 75%; width: 75%;
@include smallScreen { @include smallScreen {
width: 95%; width: 95%;
} }
// max-height: 100vh; // max-height: 100vh;
// overflow: auto; // overflow: auto;
background: #333; background: #333;
padding: 1em; padding: 1em;
margin: 1rem 0; margin: 1rem 0;
border-radius: 1.5em; border-radius: 1.5em;
text-align: center; text-align: center;
} }
} }
</style> </style>
+373 -373
View File
@@ -1,373 +1,373 @@
<template> <template>
<div class="stations_view"> <div class="stations_view">
<DonationModal :modalHidden="modalHidden" @toggleModal="toggleModal" /> <DonationModal :modalHidden="modalHidden" @toggleModal="toggleModal" />
<div class="stations_wrapper"> <div class="stations_wrapper">
<div class="stations_body"> <div class="stations_body">
<div class="body_bar"> <div class="body_bar">
<div class="bar_actions"> <div class="bar_actions">
<button <button
class="action-btn" class="action-btn"
:class="{ open: filterCardOpen }" :class="{ open: filterCardOpen }"
@click="() => toggleCardsState('filter')" @click="() => toggleCardsState('filter')"
> >
<img :src="require('@/assets/icon-filter2.svg')" alt="icon-filter" /> <img :src="require('@/assets/icon-filter2.svg')" alt="icon-filter" />
<p>FILTRY</p> <p>FILTRY</p>
</button> </button>
<button class="action-btn" @click="toggleModal"> <button class="action-btn" @click="toggleModal">
<img :src="dolarIcon" alt="icon-dolar" /> <img :src="dolarIcon" alt="icon-dolar" />
<p>WESPRZYJ</p> <p>WESPRZYJ</p>
</button> </button>
</div> </div>
<div class="bar_indicators"> <div class="bar_indicators">
<transition name="indicator-anim"> <transition name="indicator-anim">
<span <span
class="indicator_scenery-data" class="indicator_scenery-data"
v-if="data.dataConnectionStatus < 2" v-if="data.dataConnectionStatus < 2"
:class="dataStatusClass" :class="dataStatusClass"
> >
<img :src="trainIcon" alt="icon-train" /> <img :src="trainIcon" alt="icon-train" />
</span> </span>
</transition> </transition>
<transition name="indicator-anim"> <transition name="indicator-anim">
<span <span
class="indicator_timetable-data" class="indicator_timetable-data"
v-if="data.timetableDataStatus < 2" v-if="data.timetableDataStatus < 2"
:class="timetableDataStatusClass" :class="timetableDataStatusClass"
> >
<img :src="timetableIcon" alt="icon-timetable" /> <img :src="timetableIcon" alt="icon-timetable" />
</span> </span>
</transition> </transition>
</div> </div>
</div> </div>
<div class="body_table"> <div class="body_table">
<StationTable <StationTable
:stations="computedStations" :stations="computedStations"
:sorterActive="filterManager.getSorter()" :sorterActive="filterManager.getSorter()"
:setFocusedStation="setFocusedStation" :setFocusedStation="setFocusedStation"
:changeSorter="changeSorter" :changeSorter="changeSorter"
/> />
</div> </div>
</div> </div>
</div> </div>
<transition name="card-anim"> <transition name="card-anim">
<StationCard v-if="focusedStationInfo" :stationInfo="focusedStationInfo" :exit="closeCard" /> <StationCard v-if="focusedStationInfo" :stationInfo="focusedStationInfo" :exit="closeCard" />
</transition> </transition>
<transition name="card-anim"> <transition name="card-anim">
<FilterCard <FilterCard
v-if="filterCardOpen" v-if="filterCardOpen"
:exit="() => toggleCardsState('filter')" :exit="() => toggleCardsState('filter')"
@changeFilterValue="changeFilterValue" @changeFilterValue="changeFilterValue"
@resetFilters="resetFilters" @resetFilters="resetFilters"
/> />
</transition> </transition>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { Vue, Component } from "vue-property-decorator"; import { Vue, Component } from "vue-property-decorator";
import { Getter } from "vuex-class"; import { Getter } from "vuex-class";
import Station from "@/scripts/interfaces/Station"; import Station from "@/scripts/interfaces/Station";
import Train from "@/scripts/interfaces/Train"; import Train from "@/scripts/interfaces/Train";
import StorageManager from "@/scripts/storageManager"; import StorageManager from "@/scripts/storageManager";
import StationFilterManager from "@/scripts/stationFilterManager"; import StationFilterManager from "@/scripts/stationFilterManager";
import inputData from "@/data/options.json"; import inputData from "@/data/options.json";
import StationTable from "@/components/StationsView/StationTable.vue"; import StationTable from "@/components/StationsView/StationTable.vue";
import StationCard from "@/components/StationsView/StationCard.vue"; import StationCard from "@/components/StationsView/StationCard.vue";
import FilterCard from "@/components/StationsView/FilterCard.vue"; import FilterCard from "@/components/StationsView/FilterCard.vue";
import DonationModal from "@/components/Global/DonationModal.vue"; import DonationModal from "@/components/Global/DonationModal.vue";
@Component({ @Component({
components: { components: {
StationCard, StationCard,
StationTable, StationTable,
FilterCard, FilterCard,
DonationModal, DonationModal,
}, },
}) })
export default class StationsView extends Vue { export default class StationsView extends Vue {
STORAGE_KEY: string = "options_saved"; STORAGE_KEY: string = "options_saved";
STORAGE_MODAL: string = "modal"; STORAGE_MODAL: string = "modal";
trainIcon: string = require("@/assets/icon-train.svg"); trainIcon: string = require("@/assets/icon-train.svg");
timetableIcon: string = require("@/assets/icon-timetable.svg"); timetableIcon: string = require("@/assets/icon-timetable.svg");
dolarIcon: string = require("@/assets/icon-dolar.svg"); dolarIcon: string = require("@/assets/icon-dolar.svg");
filterManager: StationFilterManager = new StationFilterManager(); filterManager: StationFilterManager = new StationFilterManager();
focusedStationName: string = ""; focusedStationName: string = "";
filterCardOpen: boolean = false; filterCardOpen: boolean = false;
modalHidden: boolean = true; modalHidden: boolean = true;
inputs = inputData; inputs = inputData;
@Getter("getStationList") stationList!: Station[]; @Getter("getStationList") stationList!: Station[];
@Getter("getAllData") data; @Getter("getAllData") data;
get dataStatusClass() { get dataStatusClass() {
if (this.data.dataConnectionStatus == 0) return "loading"; if (this.data.dataConnectionStatus == 0) return "loading";
if (this.data.dataConnectionStatus == 1) return "error"; if (this.data.dataConnectionStatus == 1) return "error";
return "success"; return "success";
} }
get timetableDataStatusClass() { get timetableDataStatusClass() {
if (this.data.timetableDataStatus == 0) return "loading"; if (this.data.timetableDataStatus == 0) return "loading";
if (this.data.timetableDataStatus == 1) return "error"; if (this.data.timetableDataStatus == 1) return "error";
return "success"; return "success";
} }
mounted() { mounted() {
this.initializeOptionsStorage(); this.initializeOptionsStorage();
// this.initializeModalStorage(); // this.initializeModalStorage();
window.addEventListener("keydown", (e: KeyboardEvent) => { window.addEventListener("keydown", (e: KeyboardEvent) => {
if (e.keyCode == 27 && this.focusedStationName != "") { if (e.keyCode == 27 && this.focusedStationName != "") {
this.focusedStationName = ""; this.focusedStationName = "";
} }
}); });
} }
initializeOptionsStorage() { initializeOptionsStorage() {
if (!StorageManager.isRegistered(this.STORAGE_KEY)) return; if (!StorageManager.isRegistered(this.STORAGE_KEY)) return;
this.inputs.options.forEach((option) => { this.inputs.options.forEach((option) => {
const value = StorageManager.getBooleanValue(option.name); const value = StorageManager.getBooleanValue(option.name);
this.changeFilterValue({ name: option.name, value: value ? 0 : 1 }); this.changeFilterValue({ name: option.name, value: value ? 0 : 1 });
option.value = value; option.value = value;
}); });
this.inputs.sliders.forEach((slider) => { this.inputs.sliders.forEach((slider) => {
const value = StorageManager.getNumericValue(slider.name); const value = StorageManager.getNumericValue(slider.name);
this.changeFilterValue({ name: slider.name, value }); this.changeFilterValue({ name: slider.name, value });
slider.value = value; slider.value = value;
}); });
} }
initializeModalStorage() { initializeModalStorage() {
if (StorageManager.isRegistered(`${this.STORAGE_MODAL}_hidden`)) if (StorageManager.isRegistered(`${this.STORAGE_MODAL}_hidden`))
this.modalHidden = StorageManager.getBooleanValue( this.modalHidden = StorageManager.getBooleanValue(
`${this.STORAGE_MODAL}_hidden` `${this.STORAGE_MODAL}_hidden`
); );
} }
toggleModal() { toggleModal() {
this.modalHidden = !this.modalHidden; this.modalHidden = !this.modalHidden;
StorageManager.setBooleanValue( StorageManager.setBooleanValue(
`${this.STORAGE_MODAL}_hidden`, `${this.STORAGE_MODAL}_hidden`,
this.modalHidden this.modalHidden
); );
} }
toggleCardsState(name: string): void { toggleCardsState(name: string): void {
if (name == "filter") { if (name == "filter") {
this.filterCardOpen = !this.filterCardOpen; this.filterCardOpen = !this.filterCardOpen;
} }
} }
changeSorter(index: number) { changeSorter(index: number) {
this.filterManager.changeSorter(index); this.filterManager.changeSorter(index);
} }
changeFilterValue(filter: { name: string; value: number }) { changeFilterValue(filter: { name: string; value: number }) {
this.filterManager.changeFilterValue(filter); this.filterManager.changeFilterValue(filter);
} }
resetFilters() { resetFilters() {
this.filterManager.resetFilters(); this.filterManager.resetFilters();
} }
get computedStations() { get computedStations() {
return this.filterManager.filteredStationList(this.stationList); return this.filterManager.filteredStationList(this.stationList);
} }
closeCard() { closeCard() {
this.focusedStationName = ""; this.focusedStationName = "";
} }
setFocusedStation(name: string) { setFocusedStation(name: string) {
if (this.focusedStationName == name) this.focusedStationName = ""; if (this.focusedStationName == name) this.focusedStationName = "";
else this.focusedStationName = name; else this.focusedStationName = name;
} }
get focusedStationInfo() { get focusedStationInfo() {
return this.computedStations.find( return this.computedStations.find(
(station) => station.stationName === this.focusedStationName (station) => station.stationName === this.focusedStationName
); );
} }
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import "../styles/variables.scss"; @import "../styles/variables.scss";
@import "../styles/responsive.scss"; @import "../styles/responsive.scss";
.card-anim { .card-anim {
&-enter-active, &-enter-active,
&-leave-active { &-leave-active {
transition: all 0.25s ease-in-out; transition: all 0.25s ease-in-out;
} }
&-enter, &-enter,
&-leave-to { &-leave-to {
transform: translate(-45%, -50%); transform: translate(-45%, -50%);
opacity: 0; opacity: 0;
} }
} }
.indicator-anim { .indicator-anim {
&-enter-active, &-enter-active,
&-leave-active { &-leave-active {
transition: all 0.25s ease-in-out; transition: all 0.25s ease-in-out;
} }
&-enter, &-enter,
&-leave-to { &-leave-to {
transform: translateY(100%); transform: translateY(100%);
opacity: 0; opacity: 0;
} }
} }
.stations_view { .stations_view {
position: relative; position: relative;
padding: 1rem 0; padding: 1rem 0;
min-height: 100%; min-height: 100%;
font-size: calc(0.6rem + 0.9vw); font-size: calc(0.6rem + 0.9vw);
} }
.stations_wrapper { .stations_wrapper {
display: flex; display: flex;
justify-content: center; justify-content: center;
} }
.stations_body { .stations_body {
margin: 0 auto; margin: 0 auto;
overflow: auto; overflow: auto;
& > .body_bar { & > .body_bar {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
} }
} }
.bar_actions { .bar_actions {
display: flex; display: flex;
button { button {
margin-right: 0.5em; margin-right: 0.5em;
} }
} }
.bar_indicators { .bar_indicators {
display: flex; display: flex;
align-items: flex-end; align-items: flex-end;
> span { > span {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
width: 1.2em; width: 1.2em;
height: 1.2em; height: 1.2em;
margin-left: 0.5em; margin-left: 0.5em;
// background-color: #e68e00; // background-color: #e68e00;
border-radius: 0.5em 0.5em 0 0; border-radius: 0.5em 0.5em 0 0;
&.loading { &.loading {
background-color: $accentCol; background-color: $accentCol;
} }
&.error { &.error {
background-color: $errorCol; background-color: $errorCol;
} }
&.success { &.success {
background-color: $secondaryCol; background-color: $secondaryCol;
} }
& > img { & > img {
width: 0.9em; width: 0.9em;
animation: blinkAnim 2s ease-in-out infinite forwards; animation: blinkAnim 2s ease-in-out infinite forwards;
} }
@include smallScreen() { @include smallScreen() {
width: 1.5em; width: 1.5em;
height: 1.5em; height: 1.5em;
} }
} }
} }
.action-btn { .action-btn {
display: flex; display: flex;
align-items: center; align-items: center;
background: #333; background: #333;
border: none; border: none;
color: #e0e0e0; color: #e0e0e0;
font-size: 0.65em; font-size: 0.65em;
padding: 0.3em; padding: 0.3em;
outline: none; outline: none;
cursor: pointer; cursor: pointer;
transition: all 0.3s; transition: all 0.3s;
img { img {
width: 1.3em; width: 1.3em;
margin-right: 0.2em; margin-right: 0.2em;
} }
p { p {
font-size: 1em; font-size: 1em;
overflow: hidden; overflow: hidden;
transition: max-width 0.35s ease-in-out; transition: max-width 0.35s ease-in-out;
} }
&:hover { &:hover {
color: $accentCol; color: $accentCol;
background: rgba(#e0e0e0, 0.4); background: rgba(#e0e0e0, 0.4);
} }
&.open { &.open {
color: $accentCol; color: $accentCol;
} }
@include smallScreen() { @include smallScreen() {
font-size: 0.75rem; font-size: 0.75rem;
} }
} }
@keyframes blinkAnim { @keyframes blinkAnim {
0%, 0%,
100% { 100% {
opacity: 1; opacity: 1;
} }
50% { 50% {
opacity: 0; opacity: 0;
} }
} }
</style> </style>
File diff suppressed because it is too large Load Diff
+166 -166
View File
@@ -1,166 +1,166 @@
<template> <template>
<section class="trains-view"> <section class="trains-view">
<div class="body-wrapper"> <div class="body-wrapper">
<div class="options-wrapper"> <div class="options-wrapper">
<TrainSorter :trainList="computedTrains" @changeSorter="changeSorter" /> <TrainSorter :trainList="computedTrains" @changeSorter="changeSorter" />
<TrainSearch <TrainSearch
@changeSearchedTrain="changeSearchedTrain" @changeSearchedTrain="changeSearchedTrain"
@changeSearchedDriver="changeSearchedDriver" @changeSearchedDriver="changeSearchedDriver"
:passedSearchedTrain="passedSearchedTrain" :passedSearchedTrain="passedSearchedTrain"
:focusedTrain="focusedTrain" :focusedTrain="focusedTrain"
/> />
</div> </div>
<TrainStats :trains="trains" /> <TrainStats :trains="trains" />
<TrainTable <TrainTable
:computedTrains="computedTrains" :computedTrains="computedTrains"
@changeFocusedTrain="changeFocusedTrain" @changeFocusedTrain="changeFocusedTrain"
/> />
</div> </div>
</section> </section>
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue from "vue"; import Vue from "vue";
import { Component, Prop } from "vue-property-decorator"; import { Component, Prop } from "vue-property-decorator";
import { Getter, Action } from "vuex-class"; import { Getter, Action } from "vuex-class";
import Station from "@/scripts/interfaces/Station"; import Station from "@/scripts/interfaces/Station";
import Train from "@/scripts/interfaces/Train"; import Train from "@/scripts/interfaces/Train";
import TrainSorter from "@/components/TrainsView/TrainSorter.vue"; import TrainSorter from "@/components/TrainsView/TrainSorter.vue";
import TrainSearch from "@/components/TrainsView/TrainSearch.vue"; import TrainSearch from "@/components/TrainsView/TrainSearch.vue";
import TrainTable from "@/components/TrainsView/TrainTable.vue"; import TrainTable from "@/components/TrainsView/TrainTable.vue";
import TrainStats from "@/components/TrainsView/TrainStats.vue"; import TrainStats from "@/components/TrainsView/TrainStats.vue";
import axios from "axios"; import axios from "axios";
@Component({ @Component({
components: { components: {
TrainSorter, TrainSorter,
TrainTable, TrainTable,
TrainStats, TrainStats,
TrainSearch, TrainSearch,
}, },
}) })
export default class TrainsView extends Vue { export default class TrainsView extends Vue {
@Getter("getTrainList") trains!: Train[]; @Getter("getTrainList") trains!: Train[];
@Prop() readonly passedSearchedTrain!: string; @Prop() readonly passedSearchedTrain!: string;
sorterActive: { id: string; dir: number } = { id: "timetable", dir: 1 }; sorterActive: { id: string; dir: number } = { id: "timetable", dir: 1 };
searchedTrain: string = ""; searchedTrain: string = "";
searchedDriver: string = ""; searchedDriver: string = "";
focusedTrain: string = ""; focusedTrain: string = "";
changeSearchedTrain(trainNo: string) { changeSearchedTrain(trainNo: string) {
this.searchedTrain = trainNo; this.searchedTrain = trainNo;
} }
changeSearchedDriver(name: string) { changeSearchedDriver(name: string) {
this.searchedDriver = name; this.searchedDriver = name;
} }
changeFocusedTrain(trainNo: string) { changeFocusedTrain(trainNo: string) {
this.focusedTrain = this.focusedTrain === trainNo ? "" : trainNo; this.focusedTrain = this.focusedTrain === trainNo ? "" : trainNo;
} }
changeSorter(sorter: { id: string; dir: number }) { changeSorter(sorter: { id: string; dir: number }) {
this.sorterActive = sorter; this.sorterActive = sorter;
} }
get computedTrains() { get computedTrains() {
return this.trains return this.trains
.filter( .filter(
(train) => (train) =>
train.timetableData && train.timetableData &&
(this.searchedTrain.length > 0 (this.searchedTrain.length > 0
? train.trainNo.toString().includes(this.searchedTrain) ? train.trainNo.toString().includes(this.searchedTrain)
: true) && : true) &&
(this.searchedDriver.length > 0 (this.searchedDriver.length > 0
? train.driverName ? train.driverName
.toLowerCase() .toLowerCase()
.includes(this.searchedDriver.toLowerCase()) .includes(this.searchedDriver.toLowerCase())
: true) : true)
) )
.sort((a, b) => { .sort((a, b) => {
switch (this.sorterActive.id) { switch (this.sorterActive.id) {
case "mass": case "mass":
if (a.mass > b.mass) return this.sorterActive.dir; if (a.mass > b.mass) return this.sorterActive.dir;
else return -this.sorterActive.dir; else return -this.sorterActive.dir;
break; break;
case "distance": case "distance":
if (!a.timetableData || !b.timetableData) return 0; if (!a.timetableData || !b.timetableData) return 0;
if (a.timetableData.routeDistance > b.timetableData.routeDistance) return this.sorterActive.dir; if (a.timetableData.routeDistance > b.timetableData.routeDistance) return this.sorterActive.dir;
else return -this.sorterActive.dir; else return -this.sorterActive.dir;
break; break;
case "speed": case "speed":
if (a.speed > b.speed) return this.sorterActive.dir; if (a.speed > b.speed) return this.sorterActive.dir;
else return -this.sorterActive.dir; else return -this.sorterActive.dir;
break; break;
case "timetable": case "timetable":
if (a.trainNo > b.trainNo) return this.sorterActive.dir; if (a.trainNo > b.trainNo) return this.sorterActive.dir;
else return -this.sorterActive.dir; else return -this.sorterActive.dir;
break; break;
case "length": case "length":
if (a.length > b.length) return this.sorterActive.dir; if (a.length > b.length) return this.sorterActive.dir;
else return -this.sorterActive.dir; else return -this.sorterActive.dir;
break; break;
default: default:
break; break;
} }
return 0; return 0;
}); });
} }
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import "../styles/responsive.scss"; @import "../styles/responsive.scss";
.trains-view { .trains-view {
min-height: 100%; min-height: 100%;
position: relative; position: relative;
} }
.body-wrapper { .body-wrapper {
margin: 1rem auto; margin: 1rem auto;
max-width: 1300px; max-width: 1300px;
padding: 0 0.5rem; padding: 0 0.5rem;
font-size: calc(0.4rem + 0.4vw); font-size: calc(0.4rem + 0.4vw);
} }
.options-wrapper { .options-wrapper {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
& > div { & > div {
margin-right: 1rem; margin-right: 1rem;
} }
} }
@include bigScreen() { @include bigScreen() {
.body-wrapper { .body-wrapper {
font-size: 0.9rem; font-size: 0.9rem;
} }
} }
@include smallScreen { @include smallScreen {
.body-wrapper { .body-wrapper {
font-size: 0.8rem; font-size: 0.8rem;
} }
.options-wrapper { .options-wrapper {
justify-content: center; justify-content: center;
} }
} }
</style> </style>
+3 -3
View File
@@ -1,3 +1,3 @@
// module.exports = { // module.exports = {
// publicPath: process.env.NODE_ENV === "production" ? "/dist" : "/", // publicPath: process.env.NODE_ENV === "production" ? "/dist" : "/",
// }; // };