Dodano sygnalizator do pokazywania statusu danych

This commit is contained in:
2022-02-09 15:41:00 +01:00
parent f8a82df23f
commit c0e37310ee
7 changed files with 315 additions and 32 deletions
+15 -2
View File
@@ -75,21 +75,34 @@
// HEADER
.app_header {
display: flex;
justify-content: center;
position: relative;
background: $primaryCol;
padding: 0.5em 0.3em 0 0.3em;
border-radius: 0 0 1em 1em;
display: flex;
justify-content: center;
.signal-status-indicator {
position: absolute;
left: 50%;
bottom: 0;
transform: translateX(11.6em);
width: 1.2em;
}
}
.train-logo {
position: relative;
}
.header {
&_brand {
position: relative;
width: 100%;
+144 -7
View File
@@ -8,6 +8,14 @@
</div> -->
<header class="app_header">
<div class="header_body">
<object
class="signal-status-indicator"
type="image/svg+xml"
:data="icons.statusIndicator"
ref="status-indicator"
></object>
<!-- <object class="signal-status-indicator" :src="icons.statusIndicator" alt="status-icon" ref="status-indicator"></object> -->
<span class="header_brand">
<span>
<span>S</span>
@@ -25,7 +33,7 @@
:class="{ current: currentLang == 'pl' }"
v-if="currentLang == 'pl'"
>
<img :src="iconPL" alt="icon-pl" />
<img :src="icons.pl" alt="icon-pl" />
</span>
<span
@@ -34,7 +42,7 @@
:class="{ current: currentLang == 'en' }"
v-if="currentLang == 'en'"
>
<img :src="iconEN" alt="icon-en" />
<img :src="icons.en" alt="icon-en" />
</span>
</span>
</span>
@@ -89,12 +97,13 @@
import Clock from '@/components/App/Clock.vue';
import StorageManager from '@/scripts/managers/storageManager';
import { computed, ComputedRef, defineComponent, provide, ref } from 'vue';
import { computed, ComputedRef, defineComponent, provide, ref, watch } from 'vue';
import { GETTERS } from './constants/storeConstants';
import { StoreData } from './scripts/interfaces/StoreData';
import { useStore } from './store';
import packageInfo from '.././package.json';
import { DataStatus } from './scripts/enums/DataStatus';
export default defineComponent({
components: {
@@ -111,6 +120,9 @@ export default defineComponent({
() => store.getters[GETTERS.currentRegion]
);
const dataStatus = computed(() => data.value);
const sceneryDataStatus = computed(() => data.value.sceneryDataStatus);
const isFilterCardVisible = ref(false);
provide('isFilterCardVisible', isFilterCardVisible);
@@ -120,6 +132,10 @@ export default defineComponent({
currentRegion,
isFilterCardVisible,
dataStatus,
sceneryDataStatus,
dispatcherDataStatus: computed(() => data.value.dispatcherDataStatus),
openFilterCard() {
isFilterCardVisible.value = true;
},
@@ -132,16 +148,85 @@ export default defineComponent({
hasReleaseNotes: false,
currentLang: 'pl',
iconEN: require('@/assets/icon-en.jpg'),
iconPL: require('@/assets/icon-pl.svg'),
iconError: require('@/assets/icon-error.svg'),
svgChristmasCap: require('@/assets/christmas-cap.svg'),
icons: {
statusIndicator: require('@/assets/signal-status-indicator.svg'),
en: require('@/assets/icon-en.jpg'),
pl: require('@/assets/icon-pl.svg'),
error: require('@/assets/icon-error.svg'),
},
indicator: {} as {
status: DataStatus;
message: string;
},
}),
created() {
this.loadLang();
},
watch: {
dataStatus(storeData: StoreData) {
// if(val == DataStatus.Loaded)
// this.setSignalStatus(DataStatus.Loaded)
const dataConnectionStatus = storeData.dataConnectionStatus;
const sceneryDataStatus = storeData.sceneryDataStatus;
const trainsDataStatus = storeData.trainsDataStatus;
const dispatcherDataStatus = storeData.dispatcherDataStatus;
const timetableDataStatus = storeData.timetableDataStatus;
if (dataConnectionStatus == DataStatus.Error) {
this.indicator.status = DataStatus.Error;
this.indicator.message = "Błąd podczas łączenia z serwisem SWDR!";
this.setSignalStatus(DataStatus.Error);
return;
}
if (sceneryDataStatus == DataStatus.Error) {
this.indicator.status = DataStatus.Error;
this.indicator.message = "Nie można pobrać danych o sceneriach!";
this.setSignalStatus(DataStatus.Error);
return;
}
if (trainsDataStatus == DataStatus.Warning) {
this.indicator.status = DataStatus.Warning;
this.indicator.message = "Nie można pobrać danych o pociągach!";
this.setSignalStatus(DataStatus.Warning);
return;
}
if (dispatcherDataStatus == DataStatus.Warning) {
this.indicator.status = DataStatus.Warning;
this.indicator.message = "Nie można pobrać danych o statusach dyżurnych ruchu!";
this.setSignalStatus(DataStatus.Warning);
return;
}
if (timetableDataStatus == DataStatus.Warning) {
this.indicator.status = DataStatus.Warning;
this.indicator.message = "Rozkłady jazdy mogą być niekompletne!";
this.setSignalStatus(DataStatus.Warning);
return;
}
this.indicator.status = DataStatus.Loaded;
this.indicator.message = "";
this.setSignalStatus(DataStatus.Loaded);
},
sceneryDataStatus(val: DataStatus) {
if (val == DataStatus.Error) this.setSignalStatus(DataStatus.Error);
},
dispatcherDataStatus(val: DataStatus) {
if (val == DataStatus.Warning && this.sceneryDataStatus != DataStatus.Error)
this.setSignalStatus(DataStatus.Warning);
},
},
async mounted() {
if (StorageManager.getStringValue('version') != this.VERSION) {
StorageManager.setStringValue('version', this.VERSION);
@@ -152,6 +237,12 @@ export default defineComponent({
this.updateModalVisible = this.hasReleaseNotes && !StorageManager.getBooleanValue('version_notes_read');
this.updateToNewestVersion();
const obj = this.$refs['status-indicator'] as HTMLObjectElement;
obj.addEventListener('load', () => {
this.setSignalStatus(DataStatus.Loading);
});
},
methods: {
@@ -160,6 +251,52 @@ export default defineComponent({
StorageManager.setBooleanValue('version_notes_read', true);
},
setSignalStatus(status: DataStatus) {
const obj = this.$refs['status-indicator'] as HTMLObjectElement;
const green = obj.contentDocument?.querySelector('#green') as SVGElement;
const greenBlink = obj.contentDocument?.querySelector('#green-blink') as SVGElement;
const redTop = obj.contentDocument?.querySelector('#red-top') as SVGElement;
const orange = obj.contentDocument?.querySelector('#orange') as SVGElement;
const redBottom = obj.contentDocument?.querySelector('#red-bottom') as SVGElement;
if (status == DataStatus.Loaded) {
green.style.visibility = 'visible';
greenBlink.style.visibility = 'hidden';
redTop.style.visibility = 'hidden';
orange.style.visibility = 'hidden';
redBottom.style.visibility = 'hidden';
}
if (status == DataStatus.Warning) {
green.style.visibility = 'hidden';
greenBlink.style.visibility = 'hidden';
redTop.style.visibility = 'hidden';
orange.style.visibility = 'visible';
redBottom.style.visibility = 'hidden';
}
if (status == DataStatus.Error) {
green.style.visibility = 'hidden';
greenBlink.style.visibility = 'hidden';
redTop.style.visibility = 'visible';
orange.style.visibility = 'hidden';
redBottom.style.visibility = 'visible';
}
if (status == DataStatus.Loading) {
green.style.visibility = 'hidden';
greenBlink.style.visibility = 'visible';
redTop.style.visibility = 'hidden';
orange.style.visibility = 'hidden';
redBottom.style.visibility = 'hidden';
}
// (this.$refs['redTop'] as SVGElement).style.display = "none";
// (this.$refs['orangeBottom'] as SVGElement).style.display = "block";
// (this.$refs['redBottom'] as SVGElement).style.display = "none";
},
changeLang(lang: string) {
this.$i18n.locale = lang;
this.currentLang = lang;
+87
View File
@@ -0,0 +1,87 @@
<svg width="31" height="132" viewBox="0 0 31 132" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="status-signal-icon">
<path id="Vector 26" d="M4.5 83V99L14 110.5" stroke="#171616" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path id="Vector 27" d="M18.5 89.5L27.5 89.5L27.5 81.5" stroke="#171616" stroke-linecap="round" stroke-linejoin="round"/>
<rect id="Rectangle 13" x="11" y="83" width="8" height="49" fill="#CACACA"/>
<rect id="Rectangle 12" width="31" height="90" rx="15.5" fill="#171616"/>
<rect id="Rectangle 25" x="7" y="88" width="16" height="9.6" fill="white"/>
<g id="Group 46">
<circle id="Ellipse 13" cx="15" cy="74" r="7" fill="#393838"/>
<circle id="Ellipse 16" cx="15" cy="55" r="7" fill="#393838"/>
<circle id="Ellipse 17" cx="15" cy="36" r="7" fill="#393838"/>
<circle id="Ellipse 18" cx="15" cy="17" r="7" fill="#393838"/>
</g>
<g id="green" filter="url(#filter0_d_843_28)" style="visibility: hidden;">
<circle cx="15" cy="17" r="7" fill="#00FF0A"/>
</g>
<g id="green-blink" filter="url(#filter0_d_843_28)" style="visibility: visible;">
<circle cx="15" cy="17" r="7" fill="#00FF0A"/>
<animate
attributeType="XML"
attributeName="opacity"
values="1;0;1"
dur="1s"
repeatCount="indefinite"
/>
</g>
<g id="red-top" filter="url(#filter1_d_843_28)" style="visibility: hidden;">
<circle cx="15" cy="36" r="7" fill="#F40000"/>
</g>
<g id="orange" filter="url(#filter2_d_843_28)" style="visibility: hidden;">
<circle cx="15" cy="55" r="7" fill="#FFB800"/>
</g>
<g id="red-bottom" filter="url(#filter3_d_843_28)" style="visibility: hidden;">
<circle cx="15" cy="74" r="7" fill="#F40000"/>
<animate
attributeType="XML"
attributeName="opacity"
values="1;0;1"
dur="1s"
repeatCount="indefinite"
/>
</g>
</g>
<defs>
<filter id="filter0_d_843_28" x="3" y="5" width="24" height="24" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset/>
<feGaussianBlur stdDeviation="2.5"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 1 0 0 0 0 0.04 0 0 0 1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_843_28"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_843_28" result="shape"/>
</filter>
<filter id="filter1_d_843_28" x="3" y="24" width="24" height="24" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset/>
<feGaussianBlur stdDeviation="2.5"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.770833 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_843_28"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_843_28" result="shape"/>
</filter>
<filter id="filter2_d_843_28" x="3" y="43" width="24" height="24" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset/>
<feGaussianBlur stdDeviation="2.5"/>
<feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 0.72 0 0 0 0 0 0 0 0 1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_843_28"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_843_28" result="shape"/>
</filter>
<filter id="filter3_d_843_28" x="3" y="62" width="24" height="24" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset/>
<feGaussianBlur stdDeviation="2.5"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.770833 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_843_28"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_843_28" result="shape"/>
</filter>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

+9 -1
View File
@@ -6,9 +6,13 @@ export const ACTIONS = {
export const MUTATIONS = {
SET_SCENERY_DATA: "SET_SCENERY_DATA",
SET_DATA_CONNECTION_STATUS: "SET_DATA_CONNECTION_STATUS",
SET_SCENERY_DATA_STATUS: "SET_SCENERY_DATA_STATUS",
SET_TIMETABLE_DATA_STATUS: "SET_TIMETABLE_DATA_STATUS",
SET_DATA_CONNECTION_STATUS: "SET_DATA_CONNECTION_STATUS",
SET_DISPATCHER_DATA_STATUS: "SET_DISPATCHER_DATA_STATUS",
SET_TRAINS_DATA_STATUS: "SET_TRAINS_DATA_STATUS",
SET_REGION: "SET_REGION",
UPDATE_STATIONS: "UPDATE_STATIONS",
UPDATE_TRAINS: "UPDATE_TRAINS",
@@ -19,8 +23,12 @@ export const GETTERS = {
stationList: "stationList",
trainList: "trainList",
allData: "allData",
timetableDataStatus: "timetableDataStatus",
sceneryDataStatus: "sceneryDataStatus",
dispatcherDataStatus: "dispatcherDataStatus",
trainsDataStatus: "trainDataStatus",
dataStatus: "dataStatus",
currentRegion: "currentRegion"
}
+2 -1
View File
@@ -2,5 +2,6 @@ export const enum DataStatus {
Initialized = -1,
Loading = 0,
Error = 1,
Loaded = 2
Loaded = 2,
Warning = 3
}
+4
View File
@@ -10,5 +10,9 @@ export interface StoreData {
activeStationCount: number;
dataConnectionStatus: DataStatus;
timetableDataStatus: DataStatus;
sceneryDataStatus: DataStatus;
dispatcherDataStatus: DataStatus;
trainsDataStatus: DataStatus;
}
+54 -21
View File
@@ -36,8 +36,11 @@ export interface State {
stationCount: number;
dataConnectionStatus: DataStatus;
sceneryDataStatus: DataStatus;
timetableDataStatus: DataStatus;
dispatcherDataStatus: DataStatus;
trainsDataStatus: DataStatus;
listenerLaunched: boolean;
}
@@ -59,8 +62,11 @@ export const store = createStore<State>({
stationCount: 0,
dataConnectionStatus: DataStatus.Loading,
sceneryDataStatus: DataStatus.Loading,
timetableDataStatus: DataStatus.Loading,
dispatcherDataStatus: DataStatus.Loading,
trainsDataStatus: DataStatus.Loading,
listenerLaunched: false
}),
@@ -76,7 +82,11 @@ export const store = createStore<State>({
activeStationCount: state.stationCount,
dataConnectionStatus: state.dataConnectionStatus,
timetableDataStatus: state.timetableDataStatus
timetableDataStatus: state.timetableDataStatus,
sceneryDataStatus: state.sceneryDataStatus,
dispatcherDataStatus: state.dispatcherDataStatus,
trainsDataStatus: state.trainsDataStatus
}),
timetableDataStatus: (state): DataStatus => state.timetableDataStatus,
sceneryDataStatus: (state): DataStatus => state.sceneryDataStatus,
@@ -104,22 +114,31 @@ export const store = createStore<State>({
Promise.all([axios.get(URLs.stations), axios.get(URLs.trains), axios.get(URLs.dispatchers)])
.then(async response => {
const onlineStationsData: StationAPIData[] = response[0].data.message;
const onlineTrainsData: TrainAPIData[] = await response[1].data.message;
const onlineDispatchersData: string[][] = await response[2].data.message;
const onlineStationsData: { success: boolean, message: StationAPIData[] } = response[0].data;
const onlineTrainsData: { success: boolean, message: TrainAPIData[] } = await response[1].data;
const onlineDispatchersData: { success: boolean, message: string[][] } = await response[2].data;
const updatedStationList: Station['onlineInfo'][] = onlineStationsData.reduce((acc, station) => {
if (!onlineStationsData.success) {
commit(MUTATIONS.SET_DATA_CONNECTION_STATUS, DataStatus.Error);
commit(MUTATIONS.SET_SCENERY_DATA_STATUS, DataStatus.Error);
return;
}
commit(MUTATIONS.SET_SCENERY_DATA_STATUS, DataStatus.Loaded);
commit(MUTATIONS.SET_DISPATCHER_DATA_STATUS, onlineDispatchersData.success ? DataStatus.Loaded : DataStatus.Warning);
commit(MUTATIONS.SET_TRAINS_DATA_STATUS, onlineTrainsData.success ? DataStatus.Loaded : DataStatus.Warning);
const updatedStationList: Station['onlineInfo'][] = onlineStationsData.message.reduce((acc, station) => {
if (station.region !== this.state.region.id || !station.isOnline) return acc;
const stationStatus = onlineDispatchersData.find((status: string[]) => status[0] == station.stationHash && status[1] == this.state.region.id);
const stationStatus = onlineDispatchersData.success ? onlineDispatchersData.message.find((status: string[]) => status[0] == station.stationHash && status[1] == this.state.region.id) : undefined;
const statusTimestamp = getStatusTimestamp(stationStatus);
const statusID = getStatusID(stationStatus);
const stationTrains = onlineTrainsData
const stationTrains = onlineTrainsData.success ? onlineTrainsData.message
.filter(train => train.region === this.state.region.id && train.isOnline && train.station.stationName === station.stationName)
.map(train => ({ driverName: train.driverName, trainNo: train.trainNo }));
.map(train => ({ driverName: train.driverName, trainNo: train.trainNo })) : [];
acc.push({
name: station.stationName,
@@ -141,8 +160,8 @@ export const store = createStore<State>({
return acc;
}, [] as Station['onlineInfo'][]);
const updatedTrainList = await Promise.all(
onlineTrainsData
const updatedTrainList = onlineTrainsData.success ? await Promise.all(
onlineTrainsData.message
.filter(train => train.region === this.state.region.id)
.map(async train => {
const locoType = train.dataCon.split(";") ? train.dataCon.split(";")[0] : train.dataCon;
@@ -165,11 +184,13 @@ export const store = createStore<State>({
cars: train.dataCon.split(";").filter((train, i) => i > 0) || []
};
})
);
) : [];
// Pass reduced lists to mutations
commit(MUTATIONS.UPDATE_STATIONS, updatedStationList);
commit(MUTATIONS.UPDATE_TRAINS, updatedTrainList);
// Statuses
commit(MUTATIONS.SET_DATA_CONNECTION_STATUS, DataStatus.Loaded);
dispatch(ACTIONS.fetchTimetableData);
@@ -180,14 +201,16 @@ export const store = createStore<State>({
},
async fetchTimetableData({ commit }) {
let warnings = 0;
const reducedList = this.state.trainList.reduce(async (acc: Promise<Timetable[]>, train: Train) => {
const data: { success: boolean; message: TimetableAPIData } = await (await axios.get(URLs.getTimetableURL(train.trainNo, this.state.region.id))).data;
if (!data.success) {
warnings++;
return acc;
}
const timetable = data.message;
const trainInfo = timetable.trainInfo;
@@ -274,7 +297,7 @@ export const store = createStore<State>({
}, Promise.resolve([]));
commit(MUTATIONS.UPDATE_TIMETABLES, (await reducedList));
commit(MUTATIONS.SET_TIMETABLE_DATA_STATUS, DataStatus.Loaded);
commit(MUTATIONS.SET_TIMETABLE_DATA_STATUS, warnings == 0 ? DataStatus.Loaded : DataStatus.Warning);
}
},
@@ -293,7 +316,7 @@ export const store = createStore<State>({
supportersOnly: station[5] == "TAK",
signalType: station[6],
controlType: station[7],
SUP: station[8],
SBL: station[9],
@@ -319,18 +342,28 @@ export const store = createStore<State>({
},
SET_SCENERY_DATA_STATUS(state, status: DataStatus) {
state.sceneryDataStatus = status;
},
SET_DATA_CONNECTION_STATUS(state, status: DataStatus) {
state.dataConnectionStatus = status;
},
SET_SCENERY_DATA_STATUS(state, status: DataStatus) {
state.sceneryDataStatus = status;
},
SET_TIMETABLE_DATA_STATUS(state, status: DataStatus) {
state.timetableDataStatus = status;
},
SET_DISPATCHER_DATA_STATUS(state, status: DataStatus) {
state.dispatcherDataStatus = status;
},
SET_TRAINS_DATA_STATUS(state, status: DataStatus) {
state.trainsDataStatus = status;
},
SET_REGION(state, region: { id: string; value: string }) {
state.region = region;
},
@@ -398,7 +431,7 @@ export const store = createStore<State>({
state.stationList = state.stationList.map(station => {
const stationName = station.name.toLowerCase();
const scheduledTrains: ScheduledTrain[] = timetableList.reduce((acc: ScheduledTrain[], timetable: Timetable) => {
const scheduledTrains: ScheduledTrain[] = timetableList.reduce((acc: ScheduledTrain[], timetable: Timetable) => {
if (!timetable.followingSceneries.includes(station.onlineInfo?.hash || "")) return acc;
const stopInfoIndex = timetable.followingStops.findIndex(stop => {
@@ -406,7 +439,7 @@ export const store = createStore<State>({
// if (stop.stopName == "ARKADIA ZDRÓJ" && station.name == "Arkadia Zdrój 2019" && stop.pointId != "1583014379097") return false;
// if (stop.stopName == "ARKADIA ZDRÓJ" && station.name == "Arkadia Zdrój 2012" && stop.pointId != "1519258642187") return false;
if (stationName === stopName) return true;
if (stopName.includes(stationName) && !stop.stopName.includes("po.") && !stop.stopName.includes("podg.")) return true;
if (stationName.includes(stopName) && !stop.stopName.includes("po.") && !stop.stopName.includes("podg.")) return true;