mirror of
https://github.com/Spythere/stacjownik.git
synced 2026-05-03 21:38:13 +00:00
zmiana wyglądu statystyk dzienników
This commit is contained in:
@@ -1,128 +1,138 @@
|
|||||||
<template>
|
<template>
|
||||||
<section class="daily-stats">
|
<section class="daily-stats">
|
||||||
<span :data-active="statsStatus">
|
<span :data-active="statsStatus">
|
||||||
<b v-if="statsStatus == Status.Data.Loading">
|
<span class="stats-list">
|
||||||
{{ $t('app.loading') }}
|
|
||||||
</b>
|
|
||||||
|
|
||||||
<span class="stats-list" v-else>
|
|
||||||
<h3>
|
<h3>
|
||||||
{{ $t('journal.daily-stats-title') }}
|
{{ $t('journal.daily-stats-title') }}
|
||||||
<b class="text--primary">{{ new Date().toLocaleDateString($i18n.locale) }}</b>
|
<b class="text--primary">{{ new Date().toLocaleDateString($i18n.locale) }}</b>
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<hr style="margin-bottom: 0.5em" />
|
<hr class="header-separator" />
|
||||||
|
|
||||||
<div v-if="stats.totalTimetables">
|
<b v-if="statsStatus == Status.Data.Loading">
|
||||||
•
|
{{ $t('app.loading') }}
|
||||||
<i18n-t keypath="journal.timetable-stats-total">
|
</b>
|
||||||
<template #count>
|
|
||||||
<b class="text--primary">
|
|
||||||
{{ stats.totalTimetables }}
|
|
||||||
{{ $t('journal.timetable-count', stats.totalTimetables) }}
|
|
||||||
</b>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template #distance>
|
<b class="text--error" v-else-if="statsStatus == Status.Data.Error">
|
||||||
<b class="text--primary"> {{ stats.distanceSum?.toFixed(2) }} km</b>
|
{{ $t('journal.stats-error') }}
|
||||||
</template>
|
</b>
|
||||||
</i18n-t>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="stats.maxTimetable">
|
<b v-else-if="topDispatchers.length == 0">
|
||||||
•
|
{{ $t('journal.daily-stats-info') }}
|
||||||
<i18n-t keypath="journal.timetable-stats-longest">
|
</b>
|
||||||
<template #id>
|
|
||||||
<router-link :to="`/journal/timetables?timetableId=${stats.maxTimetable.id}`">
|
|
||||||
<b>{{ stats.maxTimetable.id }}</b>
|
|
||||||
</router-link>
|
|
||||||
</template>
|
|
||||||
<template #author>
|
|
||||||
<router-link
|
|
||||||
:to="`/journal/dispatchers?dispatcherName=${stats.maxTimetable.authorName}`"
|
|
||||||
>
|
|
||||||
<b>{{ stats.maxTimetable.authorName }}</b>
|
|
||||||
</router-link>
|
|
||||||
</template>
|
|
||||||
<template #driver>
|
|
||||||
<b class="text--primary">{{ stats.maxTimetable.driverName }}</b>
|
|
||||||
</template>
|
|
||||||
<template #distance>
|
|
||||||
<b class="text--primary">{{ stats.maxTimetable.routeDistance }} km</b>
|
|
||||||
</template>
|
|
||||||
</i18n-t>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="topDispatchers.length == 1">
|
<div v-else>
|
||||||
•
|
<div v-if="stats.totalTimetables">
|
||||||
<i18n-t keypath="journal.timetable-stats-most-active-dr">
|
•
|
||||||
<template #dispatcher>
|
<i18n-t keypath="journal.timetable-stats-total">
|
||||||
<router-link :to="`/journal/dispatchers?dispatcherName=${topDispatchers[0].name}`">
|
<template #count>
|
||||||
<b>{{ topDispatchers[0].name }}</b>
|
<b class="text--primary">
|
||||||
</router-link>
|
{{ stats.totalTimetables }}
|
||||||
</template>
|
{{ $t('journal.timetable-count', stats.totalTimetables) }}
|
||||||
<template #count>
|
</b>
|
||||||
<b class="text--primary">
|
</template>
|
||||||
{{ topDispatchers[0].count }}
|
|
||||||
{{ $t('journal.timetable-count', topDispatchers[0].count) }}
|
|
||||||
</b>
|
|
||||||
</template>
|
|
||||||
</i18n-t>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="topDispatchers.length > 1">
|
<template #distance>
|
||||||
•
|
<b class="text--primary"> {{ stats.distanceSum?.toFixed(2) }} km</b>
|
||||||
<i18n-t keypath="journal.timetable-stats-most-active-dr-many">
|
</template>
|
||||||
<template #dispatchers>
|
</i18n-t>
|
||||||
<span v-for="(disp, i) in topDispatchers" :key="i">
|
</div>
|
||||||
<span v-if="i == topDispatchers.length - 1"> {{ $t('general.and') }} </span>
|
|
||||||
|
|
||||||
<router-link :to="`/journal/dispatchers?dispatcherName=${disp.name}`">
|
<div v-if="stats.maxTimetable">
|
||||||
<b>{{ disp.name }}</b>
|
•
|
||||||
|
<i18n-t keypath="journal.timetable-stats-longest">
|
||||||
|
<template #id>
|
||||||
|
<router-link :to="`/journal/timetables?timetableId=${stats.maxTimetable.id}`">
|
||||||
|
<b>{{ stats.maxTimetable.id }}</b>
|
||||||
</router-link>
|
</router-link>
|
||||||
|
</template>
|
||||||
|
<template #author>
|
||||||
|
<router-link
|
||||||
|
:to="`/journal/dispatchers?dispatcherName=${stats.maxTimetable.authorName}`"
|
||||||
|
>
|
||||||
|
<b>{{ stats.maxTimetable.authorName }}</b>
|
||||||
|
</router-link>
|
||||||
|
</template>
|
||||||
|
<template #driver>
|
||||||
|
<b class="text--primary">{{ stats.maxTimetable.driverName }}</b>
|
||||||
|
</template>
|
||||||
|
<template #distance>
|
||||||
|
<b class="text--primary">{{ stats.maxTimetable.routeDistance }} km</b>
|
||||||
|
</template>
|
||||||
|
</i18n-t>
|
||||||
|
</div>
|
||||||
|
|
||||||
<span v-if="i < topDispatchers.length - 2">, </span>
|
<div v-if="topDispatchers.length == 1">
|
||||||
</span>
|
•
|
||||||
</template>
|
<i18n-t keypath="journal.timetable-stats-most-active-dr">
|
||||||
|
<template #dispatcher>
|
||||||
|
<router-link :to="`/journal/dispatchers?dispatcherName=${topDispatchers[0].name}`">
|
||||||
|
<b>{{ topDispatchers[0].name }}</b>
|
||||||
|
</router-link>
|
||||||
|
</template>
|
||||||
|
<template #count>
|
||||||
|
<b class="text--primary">
|
||||||
|
{{ topDispatchers[0].count }}
|
||||||
|
{{ $t('journal.timetable-count', topDispatchers[0].count) }}
|
||||||
|
</b>
|
||||||
|
</template>
|
||||||
|
</i18n-t>
|
||||||
|
</div>
|
||||||
|
|
||||||
<template #count>
|
<div v-if="topDispatchers.length > 1">
|
||||||
<b class="text--primary">
|
•
|
||||||
{{ topDispatchers[0].count }}
|
<i18n-t keypath="journal.timetable-stats-most-active-dr-many">
|
||||||
{{ $t('journal.timetable-count', topDispatchers[0].count) }}
|
<template #dispatchers>
|
||||||
</b>
|
<span v-for="(disp, i) in topDispatchers" :key="i">
|
||||||
</template>
|
<span v-if="i == topDispatchers.length - 1"> {{ $t('general.and') }} </span>
|
||||||
</i18n-t>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="stats.longestDuties.length > 0">
|
<router-link :to="`/journal/dispatchers?dispatcherName=${disp.name}`">
|
||||||
•
|
<b>{{ disp.name }}</b>
|
||||||
<i18n-t keypath="journal.timetable-stats-longest-duties">
|
</router-link>
|
||||||
<template #dispatcher>
|
|
||||||
<router-link
|
|
||||||
:to="`/journal/dispatchers?dispatcherName=${stats.longestDuties[0].name}`"
|
|
||||||
>
|
|
||||||
<b>{{ stats.longestDuties[0].name }}</b>
|
|
||||||
</router-link>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template #station>{{ stats.longestDuties[0].station }}</template>
|
<span v-if="i < topDispatchers.length - 2">, </span>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
<template #duration>
|
<template #count>
|
||||||
{{ calculateDuration(stats.longestDuties[0].duration) }}
|
<b class="text--primary">
|
||||||
</template>
|
{{ topDispatchers[0].count }}
|
||||||
</i18n-t>
|
{{ $t('journal.timetable-count', topDispatchers[0].count) }}
|
||||||
</div>
|
</b>
|
||||||
|
</template>
|
||||||
|
</i18n-t>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div v-if="stats.mostActiveDrivers.length > 0">
|
<div v-if="stats.longestDuties.length > 0">
|
||||||
•
|
•
|
||||||
<i18n-t keypath="journal.timetable-stats-most-active-driver">
|
<i18n-t keypath="journal.timetable-stats-longest-duties">
|
||||||
<template #driver>
|
<template #dispatcher>
|
||||||
<b class="text--primary">{{ stats.mostActiveDrivers[0].name }}</b>
|
<router-link
|
||||||
</template>
|
:to="`/journal/dispatchers?dispatcherName=${stats.longestDuties[0].name}`"
|
||||||
<template #distance>
|
>
|
||||||
<b class="text--primary">{{ stats.mostActiveDrivers[0].distance.toFixed(2) }} km</b>
|
<b>{{ stats.longestDuties[0].name }}</b>
|
||||||
</template>
|
</router-link>
|
||||||
</i18n-t>
|
</template>
|
||||||
|
|
||||||
|
<template #station>{{ stats.longestDuties[0].station }}</template>
|
||||||
|
|
||||||
|
<template #duration>
|
||||||
|
{{ calculateDuration(stats.longestDuties[0].duration) }}
|
||||||
|
</template>
|
||||||
|
</i18n-t>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="stats.mostActiveDrivers.length > 0">
|
||||||
|
•
|
||||||
|
<i18n-t keypath="journal.timetable-stats-most-active-driver">
|
||||||
|
<template #driver>
|
||||||
|
<b class="text--primary">{{ stats.mostActiveDrivers[0].name }}</b>
|
||||||
|
</template>
|
||||||
|
<template #distance>
|
||||||
|
<b class="text--primary">{{ stats.mostActiveDrivers[0].distance.toFixed(2) }} km</b>
|
||||||
|
</template>
|
||||||
|
</i18n-t>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
@@ -203,6 +213,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import '../../styles/responsive.scss';
|
@import '../../styles/responsive.scss';
|
||||||
|
@import '../../styles/JournalStats.scss';
|
||||||
|
|
||||||
.daily-stats {
|
.daily-stats {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
|||||||
+1
@@ -57,6 +57,7 @@ import { API } from '../../typings/api';
|
|||||||
import http from '../../http';
|
import http from '../../http';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
name: 'journal-dispatcher-stats',
|
||||||
components: { Loading },
|
components: { Loading },
|
||||||
|
|
||||||
setup() {
|
setup() {
|
||||||
@@ -1,11 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="journal-stats">
|
<div class="journal-stats" v-if="store.driverStatsData">
|
||||||
<span v-if="store.driverStatsData">
|
<span>
|
||||||
<h3>
|
<h3>
|
||||||
{{ $t('journal.stats-title') }}
|
{{ $t('journal.stats-title') }}
|
||||||
<span class="text--primary">{{ store.driverStatsName.toUpperCase() }}</span>
|
<span class="text--primary">{{ store.driverStatsName.toUpperCase() }}</span>
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
|
<hr class="header-separator" />
|
||||||
|
|
||||||
<div class="info-stats">
|
<div class="info-stats">
|
||||||
<span class="stat-badge">
|
<span class="stat-badge">
|
||||||
<span>{{ $t('journal.stats-timetables') }}</span>
|
<span>{{ $t('journal.stats-timetables') }}</span>
|
||||||
@@ -43,13 +45,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<b v-else-if="store.driverStatsStatus == Status.Data.Loading">{{
|
<!-- <b v-else-if="store.driverStatsStatus == Status.Data.Loading">{{
|
||||||
$t('journal.stats-loading')
|
$t('journal.stats-loading')
|
||||||
}}</b>
|
}}</b>
|
||||||
<b v-else-if="store.driverStatsStatus == Status.Data.Error">
|
<b v-else-if="store.driverStatsStatus == Status.Data.Error">
|
||||||
{{ $t('journal.stats-error ') }}
|
{{ $t('journal.stats-error ') }}
|
||||||
</b>
|
</b>
|
||||||
<b v-else>{{ $t('journal.driver-stats-info') }}</b>
|
<b v-else>{{ $t('journal.driver-stats-info') }}</b> -->
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,24 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="journal-stats" v-if="!store.isOffline">
|
<div
|
||||||
<div class="stats-buttons">
|
class="journal-stats dropdown"
|
||||||
|
v-if="!mainStore.isOffline"
|
||||||
|
@keydown.esc="currentStatsTab = null"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="dropdown_background"
|
||||||
|
v-if="currentStatsTab !== null"
|
||||||
|
@click="currentStatsTab = null"
|
||||||
|
></div>
|
||||||
|
|
||||||
|
<div class="actions-bar">
|
||||||
<button
|
<button
|
||||||
v-for="button in data.statsButtons"
|
v-for="button in statsButtons"
|
||||||
:key="button.name"
|
:key="button.tab"
|
||||||
class="btn--filled btn--image"
|
class="btn--filled btn--image"
|
||||||
:data-selected="button.name == currentStatsTab"
|
:data-selected="button.tab == currentStatsTab"
|
||||||
@click="onTabButtonClick(button.name)"
|
:data-disabled="button.disabled"
|
||||||
|
:disabled="button.disabled"
|
||||||
|
@click="onTabButtonClick(button.tab)"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
v-if="button.iconName"
|
v-if="button.iconName"
|
||||||
@@ -17,87 +29,69 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="stats-tab" v-show="currentStatsTab !== null">
|
<transition name="dropdown-anim">
|
||||||
<keep-alive>
|
<div class="dropdown_wrapper" v-if="currentStatsTab !== null">
|
||||||
<JournalDailyStats v-if="currentStatsTab == 'journal-daily-stats'" />
|
<keep-alive>
|
||||||
<JournalDriverStats v-else-if="currentStatsTab == 'journal-driver-stats'" />
|
<component :is="currentStatsTab" :key="currentStatsTab"></component>
|
||||||
</keep-alive>
|
</keep-alive>
|
||||||
</div>
|
</div>
|
||||||
|
</transition>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script lang="ts">
|
||||||
import { computed, onMounted, reactive, Ref, ref, watch } from 'vue';
|
import { defineComponent, PropType } from 'vue';
|
||||||
import { useMainStore } from '../../store/mainStore';
|
import { useMainStore } from '../../store/mainStore';
|
||||||
import JournalDailyStats from './JournalDailyStats.vue';
|
|
||||||
import JournalDriverStats from './JournalDriverStats.vue';
|
|
||||||
import StorageManager from '../../managers/storageManager';
|
import StorageManager from '../../managers/storageManager';
|
||||||
|
import { Journal } from './typings';
|
||||||
|
import JournalDailyStats from './JournalDailyStats.vue';
|
||||||
|
import JournalDispatcherStats from './JournalDispatcherStats.vue';
|
||||||
|
import JournalDriverStats from './JournalDriverStats.vue';
|
||||||
|
|
||||||
export type JournalStatsTab = 'journal-driver-stats' | 'journal-daily-stats';
|
export default defineComponent({
|
||||||
|
components: { JournalDailyStats, JournalDriverStats, JournalDispatcherStats },
|
||||||
const store = useMainStore();
|
props: {
|
||||||
const currentStatsTab: Ref<JournalStatsTab | null> = ref(null);
|
statsButtons: {
|
||||||
|
type: Array as PropType<Journal.StatsButton[]>,
|
||||||
let data = reactive({
|
required: true
|
||||||
statsButtons: [
|
|
||||||
{
|
|
||||||
name: 'journal-daily-stats',
|
|
||||||
localeKey: 'journal.daily-stats-title',
|
|
||||||
iconName: 'stats'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'journal-driver-stats',
|
|
||||||
localeKey: 'journal.driver-stats-title',
|
|
||||||
iconName: 'user'
|
|
||||||
}
|
}
|
||||||
] as { name: JournalStatsTab; localeKey: string; iconName?: string }[]
|
},
|
||||||
});
|
data() {
|
||||||
|
return {
|
||||||
|
Journal,
|
||||||
|
mainStore: useMainStore(),
|
||||||
|
currentStatsTab: null as Journal.StatsTab | null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
// const storedTab = StorageManager.getStringValue('journalStatsTab');
|
||||||
|
// if (storedTab && storedTab !== '' && this.statsButtons.some((b) => b.tab == storedTab))
|
||||||
|
// this.currentStatsTab = storedTab as Journal.StatsTab;
|
||||||
|
},
|
||||||
|
// watch: {
|
||||||
|
// 'mainStore.driverStatsData'(newData, prevData) {
|
||||||
|
// this.currentStatsTab =
|
||||||
|
// JSON.stringify(prevData) !== JSON.stringify(newData) && newData !== undefined
|
||||||
|
// ? Journal.StatsTab.DRIVER_STATS
|
||||||
|
// : this.currentStatsTab;
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
methods: {
|
||||||
|
onTabButtonClick(tab: Journal.StatsTab) {
|
||||||
|
this.currentStatsTab = tab == this.currentStatsTab ? null : tab;
|
||||||
|
|
||||||
function onTabButtonClick(tab: JournalStatsTab) {
|
StorageManager.setStringValue('journalStatsTab', this.currentStatsTab ?? '');
|
||||||
currentStatsTab.value = tab == currentStatsTab.value ? null : tab;
|
}
|
||||||
StorageManager.setStringValue('journalStatsTab', currentStatsTab.value ?? '');
|
|
||||||
}
|
|
||||||
|
|
||||||
watch(
|
|
||||||
computed(() => store.driverStatsData),
|
|
||||||
(newData, prevData) => {
|
|
||||||
currentStatsTab.value =
|
|
||||||
JSON.stringify(prevData) !== JSON.stringify(newData) && newData !== undefined
|
|
||||||
? 'journal-driver-stats'
|
|
||||||
: currentStatsTab.value;
|
|
||||||
}
|
}
|
||||||
);
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
const storedTab = StorageManager.getStringValue('journalStatsTab');
|
|
||||||
|
|
||||||
if (storedTab && storedTab !== '') currentStatsTab.value = storedTab as JournalStatsTab;
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import '../../styles/JournalStats.scss';
|
@import '../../styles/dropdown.scss';
|
||||||
|
@import '../../styles/dropdown_filters.scss';
|
||||||
@import '../../styles/variables.scss';
|
@import '../../styles/variables.scss';
|
||||||
|
|
||||||
.stats-buttons {
|
.dropdown_wrapper {
|
||||||
position: relative;
|
max-width: 100%;
|
||||||
|
|
||||||
display: flex;
|
|
||||||
gap: 0.5em;
|
|
||||||
|
|
||||||
margin-bottom: 0.5em;
|
|
||||||
|
|
||||||
button {
|
|
||||||
font-weight: bold;
|
|
||||||
padding: 0.5em 0.75em;
|
|
||||||
|
|
||||||
&[data-inactive='true'] {
|
|
||||||
color: gray;
|
|
||||||
}
|
|
||||||
|
|
||||||
&[data-selected='true'] {
|
|
||||||
color: $accentCol;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -46,4 +46,17 @@ export namespace Journal {
|
|||||||
id: TimetableSorterKey;
|
id: TimetableSorterKey;
|
||||||
dir: 'asc' | 'desc';
|
dir: 'asc' | 'desc';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum StatsTab {
|
||||||
|
DRIVER_STATS = 'journal-driver-stats',
|
||||||
|
DISPATCHER_STATS = 'journal-dispatcher-stats',
|
||||||
|
DAILY_STATS = 'journal-daily-stats'
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface StatsButton {
|
||||||
|
tab: StatsTab;
|
||||||
|
localeKey: string;
|
||||||
|
iconName: string;
|
||||||
|
disabled: boolean;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -371,6 +371,9 @@
|
|||||||
"driver-stats-title": "DRIVER STATS",
|
"driver-stats-title": "DRIVER STATS",
|
||||||
"driver-stats-info": "Enter a proper nickname into filters [F] to see user's driving statistics!",
|
"driver-stats-info": "Enter a proper nickname into filters [F] to see user's driving statistics!",
|
||||||
|
|
||||||
|
"dispatcher-stats-title": "DISPATCHER STATS",
|
||||||
|
"dispatcher-stats-info": "Enter a proper nickname into filters [F] to see user's dispatcher statistics!",
|
||||||
|
|
||||||
"stats-loading": "Fetching statistics...",
|
"stats-loading": "Fetching statistics...",
|
||||||
"stats-error": "Oops! An unexpected error occurred while trying to fetch statistics! :/",
|
"stats-error": "Oops! An unexpected error occurred while trying to fetch statistics! :/",
|
||||||
|
|
||||||
|
|||||||
+4
-1
@@ -349,9 +349,12 @@
|
|||||||
"daily-stats-title": "STATYSTYKI DNIA",
|
"daily-stats-title": "STATYSTYKI DNIA",
|
||||||
"daily-stats-info": "Dzisiejsze statystyki nie są jeszcze dostępne!",
|
"daily-stats-info": "Dzisiejsze statystyki nie są jeszcze dostępne!",
|
||||||
|
|
||||||
"driver-stats-title": "STATYSTYKI GRACZA",
|
"driver-stats-title": "STAT. MASZYNISTY",
|
||||||
"driver-stats-info": "Wpisz nazwę użytkownika w filtrach [F], aby zobaczyć jego statystyki maszynisty!",
|
"driver-stats-info": "Wpisz nazwę użytkownika w filtrach [F], aby zobaczyć jego statystyki maszynisty!",
|
||||||
|
|
||||||
|
"dispatcher-stats-title": "STATYSTYKI DYŻURNEGO",
|
||||||
|
"dispatcher-stats-info": "Wpisz nazwę użytkownika w filtrach [F], aby zobaczyć jego statystyki dyżurnego!",
|
||||||
|
|
||||||
"stats-loading": "Pobieranie statystyk...",
|
"stats-loading": "Pobieranie statystyk...",
|
||||||
"stats-error": "Ups! Wystąpił błąd podczas próby pobrania statystyk!",
|
"stats-error": "Ups! Wystąpił błąd podczas próby pobrania statystyk!",
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ export const useMainStore = defineStore('store', {
|
|||||||
|
|
||||||
dispatcherStatsName: '',
|
dispatcherStatsName: '',
|
||||||
dispatcherStatsData: undefined,
|
dispatcherStatsData: undefined,
|
||||||
|
dispatcherStatsStatus: Status.Data.Initialized,
|
||||||
|
|
||||||
driverStatsName: '',
|
driverStatsName: '',
|
||||||
driverStatsData: undefined,
|
driverStatsData: undefined,
|
||||||
|
|||||||
@@ -53,9 +53,7 @@
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.5em;
|
gap: 0.5em;
|
||||||
|
|
||||||
position: relative;
|
position: relative;
|
||||||
margin-bottom: 0.5em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn--load-data {
|
.btn--load-data {
|
||||||
|
|||||||
@@ -2,24 +2,30 @@
|
|||||||
@import 'responsive.scss';
|
@import 'responsive.scss';
|
||||||
|
|
||||||
.stats-tab {
|
.stats-tab {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
z-index: 99;
|
||||||
|
|
||||||
|
transform: translateY(1em);
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
background-color: #1a1a1a;
|
background-color: #1a1a1a;
|
||||||
box-shadow: 0 0 5px 1px $accentCol;
|
box-shadow: 0 0 5px 1px $accentCol;
|
||||||
|
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-end;
|
align-items: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
margin-bottom: 0.5em;
|
hr.header-separator {
|
||||||
|
margin-bottom: 1em;
|
||||||
width: 100%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.info-stats {
|
.info-stats {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 0.5em;
|
gap: 0.5em;
|
||||||
|
|
||||||
margin-top: 1em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.stat-badge {
|
.stat-badge {
|
||||||
|
|||||||
@@ -30,7 +30,8 @@
|
|||||||
top: calc(100% + 0.5em);
|
top: calc(100% + 0.5em);
|
||||||
|
|
||||||
background-color: $bgCol;
|
background-color: $bgCol;
|
||||||
box-shadow: 0 5px 10px 2px #0f0f0f;
|
// box-shadow: 0 5px 10px 2px #0f0f0f;
|
||||||
|
box-shadow: 0 0 5px 1px $accentCol;
|
||||||
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 550px;
|
max-width: 550px;
|
||||||
|
|||||||
@@ -5,11 +5,6 @@
|
|||||||
.actions-bar {
|
.actions-bar {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 0.5em;
|
gap: 0.5em;
|
||||||
margin-bottom: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.filters-options {
|
|
||||||
position: relative;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
h1.option-title {
|
h1.option-title {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
--clr-skr: #ff5100;
|
--clr-skr: #ff5100;
|
||||||
--clr-twr: #ffbb00;
|
--clr-twr: #ffbb00;
|
||||||
|
|
||||||
--clr-error: #df3e3e;
|
--clr-error: #fa3636;
|
||||||
--clr-warning: #c59429;
|
--clr-warning: #c59429;
|
||||||
|
|
||||||
--clr-donator: #f7a4ff;
|
--clr-donator: #f7a4ff;
|
||||||
@@ -158,6 +158,10 @@ ul {
|
|||||||
color: #ccc;
|
color: #ccc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&--error {
|
||||||
|
color: var(--clr-error);
|
||||||
|
}
|
||||||
|
|
||||||
&--donator {
|
&--donator {
|
||||||
color: var(--clr-donator);
|
color: var(--clr-donator);
|
||||||
text-shadow: var(--clr-donator) 0 0 10px;
|
text-shadow: var(--clr-donator) 0 0 10px;
|
||||||
@@ -183,7 +187,7 @@ a.a-button {
|
|||||||
&[data-disabled='true'] {
|
&[data-disabled='true'] {
|
||||||
user-select: none;
|
user-select: none;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
opacity: 0.85;
|
opacity: 0.7;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.btn--filled {
|
&.btn--filled {
|
||||||
|
|||||||
@@ -3,15 +3,19 @@
|
|||||||
<JournalHeader />
|
<JournalHeader />
|
||||||
|
|
||||||
<div class="journal_wrapper">
|
<div class="journal_wrapper">
|
||||||
<JournalOptions
|
<div class="journal_top-bar">
|
||||||
@on-search-confirm="fetchHistoryData"
|
<JournalOptions
|
||||||
@on-options-reset="resetOptions"
|
@on-search-confirm="fetchHistoryData"
|
||||||
@on-refresh-data="fetchHistoryData(true)"
|
@on-options-reset="resetOptions"
|
||||||
:sorter-option-ids="['timestampFrom', 'duration']"
|
@on-refresh-data="fetchHistoryData(true)"
|
||||||
:data-status="dataStatus"
|
:sorter-option-ids="['timestampFrom', 'duration']"
|
||||||
:current-options-active="currentOptionsActive"
|
:data-status="dataStatus"
|
||||||
optionsType="dispatchers"
|
:current-options-active="currentOptionsActive"
|
||||||
/>
|
optionsType="dispatchers"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<JournalStats :statsButtons="statsButtons" />
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="journal_refreshed-date" v-if="dataRefreshedAt">
|
<div class="journal_refreshed-date" v-if="dataRefreshedAt">
|
||||||
{{ $t('journal.data-refreshed-at') }}: {{ dataRefreshedAt.toLocaleString($i18n.locale) }}
|
{{ $t('journal.data-refreshed-at') }}: {{ dataRefreshedAt.toLocaleString($i18n.locale) }}
|
||||||
@@ -33,22 +37,33 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, provide, reactive, Ref, ref } from 'vue';
|
import { defineComponent, provide, reactive, Ref, ref } from 'vue';
|
||||||
|
|
||||||
import JournalOptions from '../components/JournalView/JournalOptions.vue';
|
import http from '../http';
|
||||||
import { useMainStore } from '../store/mainStore';
|
import { useMainStore } from '../store/mainStore';
|
||||||
import JournalDispatchersList from '../components/JournalView/JournalDispatchersList.vue';
|
|
||||||
|
|
||||||
import JournalHeader from '../components/JournalView/JournalHeader.vue';
|
|
||||||
import { LocationQuery } from 'vue-router';
|
import { LocationQuery } from 'vue-router';
|
||||||
import { Journal } from '../components/JournalView/typings';
|
import { Journal } from '../components/JournalView/typings';
|
||||||
import { API } from '../typings/api';
|
import { API } from '../typings/api';
|
||||||
import { Status } from '../typings/common';
|
import { Status } from '../typings/common';
|
||||||
import http from '../http';
|
|
||||||
|
import JournalOptions from '../components/JournalView/JournalOptions.vue';
|
||||||
|
import JournalDispatchersList from '../components/JournalView/JournalDispatchersList.vue';
|
||||||
|
import JournalHeader from '../components/JournalView/JournalHeader.vue';
|
||||||
|
import JournalStats from '../components/JournalView/JournalStats.vue';
|
||||||
|
|
||||||
|
const statsButtons: Journal.StatsButton[] = [
|
||||||
|
{
|
||||||
|
tab: Journal.StatsTab.DISPATCHER_STATS,
|
||||||
|
localeKey: 'journal.dispatcher-stats-title',
|
||||||
|
iconName: 'user',
|
||||||
|
disabled: false
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
JournalOptions,
|
JournalOptions,
|
||||||
JournalDispatchersList,
|
JournalDispatchersList,
|
||||||
JournalHeader
|
JournalHeader,
|
||||||
|
JournalStats
|
||||||
},
|
},
|
||||||
name: 'JournalDispatchers',
|
name: 'JournalDispatchers',
|
||||||
|
|
||||||
@@ -65,6 +80,8 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
|
|
||||||
data: () => ({
|
data: () => ({
|
||||||
|
statsButtons,
|
||||||
|
|
||||||
currentQuery: '',
|
currentQuery: '',
|
||||||
currentQueryArray: [] as string[],
|
currentQueryArray: [] as string[],
|
||||||
dataRefreshedAt: null as Date | null,
|
dataRefreshedAt: null as Date | null,
|
||||||
@@ -102,7 +119,7 @@ export default defineComponent({
|
|||||||
const scrollElement: Ref<HTMLElement | null> = ref(null);
|
const scrollElement: Ref<HTMLElement | null> = ref(null);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
store: useMainStore(),
|
mainStore: useMainStore(),
|
||||||
|
|
||||||
sorterActive,
|
sorterActive,
|
||||||
searchersValues,
|
searchersValues,
|
||||||
@@ -120,6 +137,12 @@ export default defineComponent({
|
|||||||
this.currentOptionsActive =
|
this.currentOptionsActive =
|
||||||
q.length > 2 ||
|
q.length > 2 ||
|
||||||
q.some((qv) => qv.startsWith('sortBy=') && qv.split('=')[1] != 'timestampFrom');
|
q.some((qv) => qv.startsWith('sortBy=') && qv.split('=')[1] != 'timestampFrom');
|
||||||
|
},
|
||||||
|
|
||||||
|
'mainStore.dispatcherStatsData'(stats) {
|
||||||
|
// console.log(stats);
|
||||||
|
// this.statsButtons.find((sb) => sb.tab == Journal.StatsTab.DRIVER_STATS)!.disabled =
|
||||||
|
// driverStats === undefined;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -241,7 +264,7 @@ export default defineComponent({
|
|||||||
this.historyList = responseData;
|
this.historyList = responseData;
|
||||||
|
|
||||||
// Stats display
|
// Stats display
|
||||||
this.store.dispatcherStatsName =
|
this.mainStore.dispatcherStatsName =
|
||||||
this.historyList.length > 0 && this.searchersValues['search-dispatcher'].trim()
|
this.historyList.length > 0 && this.searchersValues['search-dispatcher'].trim()
|
||||||
? this.historyList[0].dispatcherName
|
? this.historyList[0].dispatcherName
|
||||||
: '';
|
: '';
|
||||||
|
|||||||
@@ -3,18 +3,20 @@
|
|||||||
<JournalHeader />
|
<JournalHeader />
|
||||||
|
|
||||||
<div class="journal_wrapper">
|
<div class="journal_wrapper">
|
||||||
<JournalOptions
|
<div class="journal_top-bar">
|
||||||
@on-search-confirm="fetchHistoryData"
|
<JournalOptions
|
||||||
@on-options-reset="resetOptions"
|
@on-search-confirm="fetchHistoryData"
|
||||||
@on-refresh-data="fetchHistoryData"
|
@on-options-reset="resetOptions"
|
||||||
:sorter-option-ids="['timetableId', 'beginDate', 'routeDistance', 'allStopsCount']"
|
@on-refresh-data="fetchHistoryData"
|
||||||
:filters="journalTimetableFilters"
|
:sorter-option-ids="['timetableId', 'beginDate', 'routeDistance', 'allStopsCount']"
|
||||||
:currentOptionsActive="currentOptionsActive"
|
:filters="journalTimetableFilters"
|
||||||
:data-status="dataStatus"
|
:currentOptionsActive="currentOptionsActive"
|
||||||
optionsType="timetables"
|
:data-status="dataStatus"
|
||||||
/>
|
optionsType="timetables"
|
||||||
|
/>
|
||||||
|
|
||||||
<JournalStats />
|
<JournalStats :statsButtons="statsButtons" />
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="journal_refreshed-date" v-if="dataRefreshedAt">
|
<div class="journal_refreshed-date" v-if="dataRefreshedAt">
|
||||||
{{ $t('journal.data-refreshed-at') }}: {{ dataRefreshedAt.toLocaleString($i18n.locale) }}
|
{{ $t('journal.data-refreshed-at') }}: {{ dataRefreshedAt.toLocaleString($i18n.locale) }}
|
||||||
@@ -138,6 +140,24 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
|
|
||||||
data: () => ({
|
data: () => ({
|
||||||
|
journalTimetableFilters,
|
||||||
|
mainStore: useMainStore(),
|
||||||
|
|
||||||
|
statsButtons: [
|
||||||
|
{
|
||||||
|
tab: Journal.StatsTab.DAILY_STATS,
|
||||||
|
localeKey: 'journal.daily-stats-title',
|
||||||
|
iconName: 'stats',
|
||||||
|
disabled: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tab: Journal.StatsTab.DRIVER_STATS,
|
||||||
|
localeKey: 'journal.driver-stats-title',
|
||||||
|
iconName: 'user',
|
||||||
|
disabled: true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
currentQueryParams: {} as TimetablesQueryParams,
|
currentQueryParams: {} as TimetablesQueryParams,
|
||||||
dataRefreshedAt: null as Date | null,
|
dataRefreshedAt: null as Date | null,
|
||||||
|
|
||||||
@@ -149,7 +169,6 @@ export default defineComponent({
|
|||||||
currentOptionsActive: false,
|
currentOptionsActive: false,
|
||||||
|
|
||||||
timetableHistory: [] as API.TimetableHistory.Response,
|
timetableHistory: [] as API.TimetableHistory.Response,
|
||||||
journalTimetableFilters,
|
|
||||||
|
|
||||||
dataStatus: Status.Data.Loading,
|
dataStatus: Status.Data.Loading,
|
||||||
dataErrorMessage: ''
|
dataErrorMessage: ''
|
||||||
@@ -189,15 +208,18 @@ export default defineComponent({
|
|||||||
countFromIndex,
|
countFromIndex,
|
||||||
countLimit,
|
countLimit,
|
||||||
|
|
||||||
scrollElement,
|
scrollElement
|
||||||
|
|
||||||
store: useMainStore()
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
currentQueryParams(q: TimetablesQueryParams) {
|
currentQueryParams(q: TimetablesQueryParams) {
|
||||||
this.currentOptionsActive = Object.values(q).some((v) => v !== undefined);
|
this.currentOptionsActive = Object.values(q).some((v) => v !== undefined);
|
||||||
|
},
|
||||||
|
|
||||||
|
'mainStore.driverStatsData'(driverStats) {
|
||||||
|
this.statsButtons.find((sb) => sb.tab == Journal.StatsTab.DRIVER_STATS)!.disabled =
|
||||||
|
driverStats === undefined;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user