mirror of
https://github.com/Spythere/stacjownik.git
synced 2026-05-03 05:18:11 +00:00
zmiana wyglądu statystyk dzienników
This commit is contained in:
@@ -1,128 +1,138 @@
|
||||
<template>
|
||||
<section class="daily-stats">
|
||||
<span :data-active="statsStatus">
|
||||
<b v-if="statsStatus == Status.Data.Loading">
|
||||
{{ $t('app.loading') }}
|
||||
</b>
|
||||
|
||||
<span class="stats-list" v-else>
|
||||
<span class="stats-list">
|
||||
<h3>
|
||||
{{ $t('journal.daily-stats-title') }}
|
||||
<b class="text--primary">{{ new Date().toLocaleDateString($i18n.locale) }}</b>
|
||||
</h3>
|
||||
|
||||
<hr style="margin-bottom: 0.5em" />
|
||||
<hr class="header-separator" />
|
||||
|
||||
<div v-if="stats.totalTimetables">
|
||||
•
|
||||
<i18n-t keypath="journal.timetable-stats-total">
|
||||
<template #count>
|
||||
<b class="text--primary">
|
||||
{{ stats.totalTimetables }}
|
||||
{{ $t('journal.timetable-count', stats.totalTimetables) }}
|
||||
</b>
|
||||
</template>
|
||||
<b v-if="statsStatus == Status.Data.Loading">
|
||||
{{ $t('app.loading') }}
|
||||
</b>
|
||||
|
||||
<template #distance>
|
||||
<b class="text--primary"> {{ stats.distanceSum?.toFixed(2) }} km</b>
|
||||
</template>
|
||||
</i18n-t>
|
||||
</div>
|
||||
<b class="text--error" v-else-if="statsStatus == Status.Data.Error">
|
||||
{{ $t('journal.stats-error') }}
|
||||
</b>
|
||||
|
||||
<div v-if="stats.maxTimetable">
|
||||
•
|
||||
<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>
|
||||
</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>
|
||||
<b v-else-if="topDispatchers.length == 0">
|
||||
{{ $t('journal.daily-stats-info') }}
|
||||
</b>
|
||||
|
||||
<div v-if="topDispatchers.length == 1">
|
||||
•
|
||||
<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>
|
||||
<div v-else>
|
||||
<div v-if="stats.totalTimetables">
|
||||
•
|
||||
<i18n-t keypath="journal.timetable-stats-total">
|
||||
<template #count>
|
||||
<b class="text--primary">
|
||||
{{ stats.totalTimetables }}
|
||||
{{ $t('journal.timetable-count', stats.totalTimetables) }}
|
||||
</b>
|
||||
</template>
|
||||
|
||||
<div v-if="topDispatchers.length > 1">
|
||||
•
|
||||
<i18n-t keypath="journal.timetable-stats-most-active-dr-many">
|
||||
<template #dispatchers>
|
||||
<span v-for="(disp, i) in topDispatchers" :key="i">
|
||||
<span v-if="i == topDispatchers.length - 1"> {{ $t('general.and') }} </span>
|
||||
<template #distance>
|
||||
<b class="text--primary"> {{ stats.distanceSum?.toFixed(2) }} km</b>
|
||||
</template>
|
||||
</i18n-t>
|
||||
</div>
|
||||
|
||||
<router-link :to="`/journal/dispatchers?dispatcherName=${disp.name}`">
|
||||
<b>{{ disp.name }}</b>
|
||||
<div v-if="stats.maxTimetable">
|
||||
•
|
||||
<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>
|
||||
</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>
|
||||
</span>
|
||||
</template>
|
||||
<div v-if="topDispatchers.length == 1">
|
||||
•
|
||||
<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>
|
||||
<b class="text--primary">
|
||||
{{ topDispatchers[0].count }}
|
||||
{{ $t('journal.timetable-count', topDispatchers[0].count) }}
|
||||
</b>
|
||||
</template>
|
||||
</i18n-t>
|
||||
</div>
|
||||
<div v-if="topDispatchers.length > 1">
|
||||
•
|
||||
<i18n-t keypath="journal.timetable-stats-most-active-dr-many">
|
||||
<template #dispatchers>
|
||||
<span v-for="(disp, i) in topDispatchers" :key="i">
|
||||
<span v-if="i == topDispatchers.length - 1"> {{ $t('general.and') }} </span>
|
||||
|
||||
<div v-if="stats.longestDuties.length > 0">
|
||||
•
|
||||
<i18n-t keypath="journal.timetable-stats-longest-duties">
|
||||
<template #dispatcher>
|
||||
<router-link
|
||||
:to="`/journal/dispatchers?dispatcherName=${stats.longestDuties[0].name}`"
|
||||
>
|
||||
<b>{{ stats.longestDuties[0].name }}</b>
|
||||
</router-link>
|
||||
</template>
|
||||
<router-link :to="`/journal/dispatchers?dispatcherName=${disp.name}`">
|
||||
<b>{{ disp.name }}</b>
|
||||
</router-link>
|
||||
|
||||
<template #station>{{ stats.longestDuties[0].station }}</template>
|
||||
<span v-if="i < topDispatchers.length - 2">, </span>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<template #duration>
|
||||
{{ calculateDuration(stats.longestDuties[0].duration) }}
|
||||
</template>
|
||||
</i18n-t>
|
||||
</div>
|
||||
<template #count>
|
||||
<b class="text--primary">
|
||||
{{ topDispatchers[0].count }}
|
||||
{{ $t('journal.timetable-count', topDispatchers[0].count) }}
|
||||
</b>
|
||||
</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 v-if="stats.longestDuties.length > 0">
|
||||
•
|
||||
<i18n-t keypath="journal.timetable-stats-longest-duties">
|
||||
<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>
|
||||
|
||||
<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>
|
||||
</span>
|
||||
</span>
|
||||
@@ -203,6 +213,7 @@ export default defineComponent({
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../../styles/responsive.scss';
|
||||
@import '../../styles/JournalStats.scss';
|
||||
|
||||
.daily-stats {
|
||||
text-align: left;
|
||||
|
||||
+1
@@ -57,6 +57,7 @@ import { API } from '../../typings/api';
|
||||
import http from '../../http';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'journal-dispatcher-stats',
|
||||
components: { Loading },
|
||||
|
||||
setup() {
|
||||
@@ -1,11 +1,13 @@
|
||||
<template>
|
||||
<div class="journal-stats">
|
||||
<span v-if="store.driverStatsData">
|
||||
<div class="journal-stats" v-if="store.driverStatsData">
|
||||
<span>
|
||||
<h3>
|
||||
{{ $t('journal.stats-title') }}
|
||||
<span class="text--primary">{{ store.driverStatsName.toUpperCase() }}</span>
|
||||
</h3>
|
||||
|
||||
<hr class="header-separator" />
|
||||
|
||||
<div class="info-stats">
|
||||
<span class="stat-badge">
|
||||
<span>{{ $t('journal.stats-timetables') }}</span>
|
||||
@@ -43,13 +45,13 @@
|
||||
</div>
|
||||
</span>
|
||||
|
||||
<b v-else-if="store.driverStatsStatus == Status.Data.Loading">{{
|
||||
<!-- <b v-else-if="store.driverStatsStatus == Status.Data.Loading">{{
|
||||
$t('journal.stats-loading')
|
||||
}}</b>
|
||||
<b v-else-if="store.driverStatsStatus == Status.Data.Error">
|
||||
{{ $t('journal.stats-error ') }}
|
||||
</b>
|
||||
<b v-else>{{ $t('journal.driver-stats-info') }}</b>
|
||||
<b v-else>{{ $t('journal.driver-stats-info') }}</b> -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -1,12 +1,24 @@
|
||||
<template>
|
||||
<div class="journal-stats" v-if="!store.isOffline">
|
||||
<div class="stats-buttons">
|
||||
<div
|
||||
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
|
||||
v-for="button in data.statsButtons"
|
||||
:key="button.name"
|
||||
v-for="button in statsButtons"
|
||||
:key="button.tab"
|
||||
class="btn--filled btn--image"
|
||||
:data-selected="button.name == currentStatsTab"
|
||||
@click="onTabButtonClick(button.name)"
|
||||
:data-selected="button.tab == currentStatsTab"
|
||||
:data-disabled="button.disabled"
|
||||
:disabled="button.disabled"
|
||||
@click="onTabButtonClick(button.tab)"
|
||||
>
|
||||
<img
|
||||
v-if="button.iconName"
|
||||
@@ -17,87 +29,69 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="stats-tab" v-show="currentStatsTab !== null">
|
||||
<keep-alive>
|
||||
<JournalDailyStats v-if="currentStatsTab == 'journal-daily-stats'" />
|
||||
<JournalDriverStats v-else-if="currentStatsTab == 'journal-driver-stats'" />
|
||||
</keep-alive>
|
||||
</div>
|
||||
<transition name="dropdown-anim">
|
||||
<div class="dropdown_wrapper" v-if="currentStatsTab !== null">
|
||||
<keep-alive>
|
||||
<component :is="currentStatsTab" :key="currentStatsTab"></component>
|
||||
</keep-alive>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, reactive, Ref, ref, watch } from 'vue';
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType } from 'vue';
|
||||
import { useMainStore } from '../../store/mainStore';
|
||||
import JournalDailyStats from './JournalDailyStats.vue';
|
||||
import JournalDriverStats from './JournalDriverStats.vue';
|
||||
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';
|
||||
|
||||
const store = useMainStore();
|
||||
const currentStatsTab: Ref<JournalStatsTab | null> = ref(null);
|
||||
|
||||
let data = reactive({
|
||||
statsButtons: [
|
||||
{
|
||||
name: 'journal-daily-stats',
|
||||
localeKey: 'journal.daily-stats-title',
|
||||
iconName: 'stats'
|
||||
},
|
||||
{
|
||||
name: 'journal-driver-stats',
|
||||
localeKey: 'journal.driver-stats-title',
|
||||
iconName: 'user'
|
||||
export default defineComponent({
|
||||
components: { JournalDailyStats, JournalDriverStats, JournalDispatcherStats },
|
||||
props: {
|
||||
statsButtons: {
|
||||
type: Array as PropType<Journal.StatsButton[]>,
|
||||
required: true
|
||||
}
|
||||
] 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) {
|
||||
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;
|
||||
StorageManager.setStringValue('journalStatsTab', this.currentStatsTab ?? '');
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
const storedTab = StorageManager.getStringValue('journalStatsTab');
|
||||
|
||||
if (storedTab && storedTab !== '') currentStatsTab.value = storedTab as JournalStatsTab;
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../../styles/JournalStats.scss';
|
||||
@import '../../styles/dropdown.scss';
|
||||
@import '../../styles/dropdown_filters.scss';
|
||||
@import '../../styles/variables.scss';
|
||||
|
||||
.stats-buttons {
|
||||
position: relative;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
.dropdown_wrapper {
|
||||
max-width: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -46,4 +46,17 @@ export namespace Journal {
|
||||
id: TimetableSorterKey;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user