mirror of
https://github.com/Spythere/stacjownik.git
synced 2026-05-03 05:18:11 +00:00
chore: dispatcher history revamp & statuses
This commit is contained in:
@@ -88,8 +88,9 @@ $unknown: #b93c3c;
|
|||||||
.status-badge {
|
.status-badge {
|
||||||
border-radius: 1em;
|
border-radius: 1em;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
text-wrap: nowrap;
|
||||||
|
|
||||||
padding: 0.2em 0.55em;
|
padding: 0.2rem 0.55rem;
|
||||||
|
|
||||||
background-color: $online;
|
background-color: $online;
|
||||||
|
|
||||||
@@ -106,13 +107,13 @@ $unknown: #b93c3c;
|
|||||||
|
|
||||||
&.no-limit {
|
&.no-limit {
|
||||||
background-color: $no-limit;
|
background-color: $no-limit;
|
||||||
font-size: 0.85em;
|
font-size: 0.9em;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.not-signed,
|
&.not-signed,
|
||||||
&.unavailable {
|
&.unavailable {
|
||||||
background-color: $unav;
|
background-color: $unav;
|
||||||
font-size: 0.85em;
|
font-size: 0.9em;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.afk {
|
&.afk {
|
||||||
@@ -125,7 +126,7 @@ $unknown: #b93c3c;
|
|||||||
background-color: $no-space;
|
background-color: $no-space;
|
||||||
border: 1px solid white;
|
border: 1px solid white;
|
||||||
color: white;
|
color: white;
|
||||||
font-size: 0.85em;
|
font-size: 0.9em;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.unknown,
|
&.unknown,
|
||||||
|
|||||||
@@ -15,90 +15,118 @@
|
|||||||
{{ $t('app.no-result') }}
|
{{ $t('app.no-result') }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else>
|
<div v-else class="history-list">
|
||||||
<table class="dispatchers-table">
|
<div v-for="historyItem in dispatcherHistory" :key="historyItem.id" class="history-item">
|
||||||
<thead>
|
<div class="item-top">
|
||||||
<th>{{ $t('journal.history-name') }}</th>
|
<span>
|
||||||
<th>{{ $t('journal.history-hash') }}</th>
|
<span>
|
||||||
<th>{{ $t('journal.history-dispatcher') }}</th>
|
<router-link :to="`/journal/dispatchers?search-station=${historyItem.stationName}`">
|
||||||
<th>{{ $t('journal.history-level') }}</th>
|
<b>{{ historyItem.stationName }}</b>
|
||||||
<th>{{ $t('journal.history-rate') }}</th>
|
</router-link>
|
||||||
<th>{{ $t('journal.history-region') }}</th>
|
|
||||||
<th>{{ $t('journal.history-date') }}</th>
|
|
||||||
</thead>
|
|
||||||
|
|
||||||
<tbody>
|
<b class="text--grayed"> #{{ historyItem.stationHash }}</b>
|
||||||
<transition-group name="list-anim">
|
</span>
|
||||||
<tr v-for="historyItem in dispatcherHistory" :key="historyItem.id">
|
•
|
||||||
<td>
|
<b
|
||||||
<router-link
|
v-if="historyItem.dispatcherLevel !== null"
|
||||||
:to="`/journal/dispatchers?search-station=${historyItem.stationName}`"
|
class="level-badge dispatcher"
|
||||||
>
|
:style="
|
||||||
<b>{{ historyItem.stationName }}</b>
|
calculateExpStyle(historyItem.dispatcherLevel, historyItem.dispatcherIsSupporter)
|
||||||
</router-link>
|
"
|
||||||
</td>
|
>
|
||||||
<td>#{{ historyItem.stationHash }}</td>
|
{{ historyItem.dispatcherLevel >= 2 ? historyItem.dispatcherLevel : 'L' }}
|
||||||
<td>
|
</b>
|
||||||
|
<b style="margin-left: 5px">
|
||||||
|
<span
|
||||||
|
v-if="apiStore.donatorsData.includes(historyItem.dispatcherName)"
|
||||||
|
data-tooltip-type="DonatorTooltip"
|
||||||
|
:data-tooltip-content="$t('donations.dispatcher-message')"
|
||||||
|
>
|
||||||
<router-link
|
<router-link
|
||||||
|
class="text--donator"
|
||||||
:to="`/journal/dispatchers?search-dispatcher=${historyItem.dispatcherName}`"
|
:to="`/journal/dispatchers?search-dispatcher=${historyItem.dispatcherName}`"
|
||||||
>
|
>
|
||||||
<b
|
{{ historyItem.dispatcherName }}
|
||||||
v-if="apiStore.donatorsData.includes(historyItem.dispatcherName)"
|
|
||||||
class="text--donator"
|
|
||||||
:title="$t('donations.dispatcher-message')"
|
|
||||||
>
|
|
||||||
{{ historyItem.dispatcherName }}
|
|
||||||
</b>
|
|
||||||
|
|
||||||
<b v-else>
|
|
||||||
{{ historyItem.dispatcherName }}
|
|
||||||
</b>
|
|
||||||
</router-link>
|
</router-link>
|
||||||
</td>
|
</span>
|
||||||
<td>
|
|
||||||
|
<router-link
|
||||||
|
v-else
|
||||||
|
:to="`/journal/dispatchers?search-dispatcher=${historyItem.dispatcherName}`"
|
||||||
|
>
|
||||||
|
{{ historyItem.dispatcherName }}
|
||||||
|
</router-link>
|
||||||
|
</b>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<span v-if="historyItem.timestampTo">
|
||||||
|
<b>{{ $d(historyItem.timestampFrom) }}</b>
|
||||||
|
{{ timestampToString(historyItem.timestampFrom) }}
|
||||||
|
-
|
||||||
<b
|
<b
|
||||||
v-if="historyItem.dispatcherLevel !== null"
|
v-if="
|
||||||
class="level-badge dispatcher"
|
new Date(historyItem.timestampFrom).getDate() !=
|
||||||
:style="
|
new Date(historyItem.timestampTo).getDate()
|
||||||
calculateExpStyle(
|
|
||||||
historyItem.dispatcherLevel,
|
|
||||||
historyItem.dispatcherIsSupporter
|
|
||||||
)
|
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
{{ historyItem.dispatcherLevel >= 2 ? historyItem.dispatcherLevel : 'L' }}
|
{{ $d(historyItem.timestampTo) }}
|
||||||
</b>
|
</b>
|
||||||
</td>
|
{{ timestampToString(historyItem.timestampTo) }} ({{
|
||||||
<td class="text--primary">
|
calculateDuration(historyItem.currentDuration)
|
||||||
<b>{{ historyItem.dispatcherRate }}</b>
|
}})
|
||||||
</td>
|
</span>
|
||||||
<td>
|
|
||||||
<b class="region-badge" :aria-describedby="historyItem.region">{{
|
<router-link
|
||||||
regions.find((r) => r.id == historyItem.region)?.value || '???'
|
:to="`/scenery?station=${historyItem.stationName}`"
|
||||||
}}</b>
|
class="dispatcher-online"
|
||||||
</td>
|
v-else
|
||||||
<td style="min-width: 200px" class="time">
|
>
|
||||||
<span v-if="historyItem.timestampTo" class="text--offline">
|
{{ $t('journal.online-since') }}
|
||||||
<b>{{ $d(historyItem.timestampFrom) }}</b>
|
<b>
|
||||||
|
{{
|
||||||
|
new Date().getDate() != new Date(historyItem.timestampFrom).getDate()
|
||||||
|
? $d(historyItem.timestampFrom)
|
||||||
|
: ''
|
||||||
|
}}
|
||||||
{{ timestampToString(historyItem.timestampFrom) }}
|
{{ timestampToString(historyItem.timestampFrom) }}
|
||||||
- {{ timestampToString(historyItem.timestampTo) }} ({{
|
</b>
|
||||||
calculateDuration(historyItem.currentDuration)
|
({{ calculateDuration(historyItem.currentDuration) }})
|
||||||
}})
|
</router-link>
|
||||||
</span>
|
</div>
|
||||||
<span class="dispatcher-online" v-else>
|
</span>
|
||||||
<b class="text--online">
|
|
||||||
<router-link :to="`/scenery?station=${historyItem.stationName}`">{{
|
<span class="item-top-right">
|
||||||
$t('journal.online-since')
|
<div>
|
||||||
}}</router-link>
|
<span>
|
||||||
{{ timestampToString(historyItem.timestampFrom) }}
|
{{ $t('scenery.dispatcher-rate') }}
|
||||||
</b>
|
<b class="text--primary"> {{ historyItem.dispatcherRate }}</b>
|
||||||
({{ calculateDuration(historyItem.currentDuration) }})
|
</span>
|
||||||
</span>
|
<button class="btn btn--option" @click="toggleExtraInfo(historyItem.id)">
|
||||||
</td>
|
{{ $t('scenery.dispatcher-status-changes') }}
|
||||||
</tr>
|
<b class="text--primary">{{ historyItem.statusHistory.length }}</b>
|
||||||
</transition-group>
|
</button>
|
||||||
</tbody>
|
</div>
|
||||||
</table>
|
|
||||||
|
<b class="region-badge" :aria-describedby="historyItem.region">
|
||||||
|
REGION: {{ regions.find((r) => r.id == historyItem.region)?.name }}
|
||||||
|
</b>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="item-bottom" v-if="openItemIndexes.includes(historyItem.id)">
|
||||||
|
<div class="history-statuses">
|
||||||
|
<div v-for="statusItem in historyItem.statusHistory">
|
||||||
|
<b style="margin-right: 0.5em">{{
|
||||||
|
timestampToString(parseInt(statusItem.split('@')[0]))
|
||||||
|
}}</b>
|
||||||
|
<StationStatusBadge
|
||||||
|
:dispatcher-status="Number(statusItem.split('@')[1])"
|
||||||
|
:is-online="true"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<AddDataButton
|
<AddDataButton
|
||||||
:list="dispatcherHistory"
|
:list="dispatcherHistory"
|
||||||
@@ -127,12 +155,13 @@ import { API } from '../../../typings/api';
|
|||||||
import { Status } from '../../../typings/common';
|
import { Status } from '../../../typings/common';
|
||||||
import Loading from '../../Global/Loading.vue';
|
import Loading from '../../Global/Loading.vue';
|
||||||
import AddDataButton from '../../Global/AddDataButton.vue';
|
import AddDataButton from '../../Global/AddDataButton.vue';
|
||||||
|
import StationStatusBadge from '../../Global/StationStatusBadge.vue';
|
||||||
import dateMixin from '../../../mixins/dateMixin';
|
import dateMixin from '../../../mixins/dateMixin';
|
||||||
import styleMixin from '../../../mixins/styleMixin';
|
import styleMixin from '../../../mixins/styleMixin';
|
||||||
import { useApiStore } from '../../../store/apiStore';
|
import { useApiStore } from '../../../store/apiStore';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: { Loading, AddDataButton },
|
components: { Loading, AddDataButton, StationStatusBadge },
|
||||||
|
|
||||||
mixins: [dateMixin, styleMixin],
|
mixins: [dateMixin, styleMixin],
|
||||||
|
|
||||||
@@ -160,39 +189,18 @@ export default defineComponent({
|
|||||||
Status,
|
Status,
|
||||||
store: useMainStore(),
|
store: useMainStore(),
|
||||||
apiStore: useApiStore(),
|
apiStore: useApiStore(),
|
||||||
regions
|
regions,
|
||||||
|
|
||||||
|
openItemIndexes: [] as number[]
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
|
||||||
computedDispatcherHistory() {
|
|
||||||
return this.dispatcherHistory.reduce(
|
|
||||||
(acc, historyItem, i) => {
|
|
||||||
if (this.isAnotherDay(i - 1, i))
|
|
||||||
acc.push(new Date(historyItem.timestampFrom).toLocaleDateString('pl-PL'));
|
|
||||||
acc.push(historyItem);
|
|
||||||
|
|
||||||
return acc;
|
|
||||||
},
|
|
||||||
[] as (API.DispatcherHistory.Data | string)[]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
navigateToScenery(name: string, isOnline: boolean) {
|
toggleExtraInfo(id: number) {
|
||||||
if (!isOnline) return;
|
const existingId = this.openItemIndexes.indexOf(id);
|
||||||
|
|
||||||
this.$router.push(`/scenery?station=${name.trim().replace(/ /g, '_')}`);
|
if (existingId != -1) this.openItemIndexes.splice(existingId, 1);
|
||||||
},
|
else this.openItemIndexes.push(id);
|
||||||
|
|
||||||
isAnotherDay(prevIndex: number, currIndex: number) {
|
|
||||||
if (currIndex == 0) return true;
|
|
||||||
|
|
||||||
return (
|
|
||||||
new Date(this.dispatcherHistory[prevIndex].timestampFrom).getDate() !=
|
|
||||||
new Date(this.dispatcherHistory[currIndex].timestampFrom).getDate()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -205,53 +213,71 @@ export default defineComponent({
|
|||||||
@import '../../../styles/variables.scss';
|
@import '../../../styles/variables.scss';
|
||||||
@import '../../../styles/JournalSection.scss';
|
@import '../../../styles/JournalSection.scss';
|
||||||
|
|
||||||
table.dispatchers-table {
|
.history-list {
|
||||||
--_bg-table: #111;
|
display: flex;
|
||||||
--_bg-head: #101010;
|
flex-direction: column;
|
||||||
--_bg-row: #2f2f2f;
|
gap: 0.5em;
|
||||||
|
text-align: left;
|
||||||
width: 100%;
|
|
||||||
border-collapse: collapse;
|
|
||||||
position: relative;
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
margin-bottom: 1em;
|
|
||||||
|
|
||||||
thead {
|
|
||||||
position: sticky;
|
|
||||||
top: 0;
|
|
||||||
background-color: var(--_bg-head);
|
|
||||||
}
|
|
||||||
|
|
||||||
th {
|
|
||||||
padding: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
tr {
|
|
||||||
background-color: var(--_bg-row);
|
|
||||||
border-bottom: 2px solid black;
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
td {
|
|
||||||
padding: 0.75em;
|
|
||||||
|
|
||||||
.level-badge {
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.text {
|
.region-badge {
|
||||||
&--online {
|
padding: 0 0.25em;
|
||||||
color: springgreen;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
&--offline {
|
.level-badge {
|
||||||
color: #ddd;
|
text-align: center;
|
||||||
|
display: inline-block;
|
||||||
|
line-height: 1.6em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dispatcher-online {
|
||||||
|
color: springgreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-item {
|
||||||
|
background-color: #1a1a1a;
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-top {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
line-height: 1.75em;
|
||||||
|
gap: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-top-right {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
gap: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-bottom {
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-statuses {
|
||||||
|
display: flex;
|
||||||
|
overflow: auto;
|
||||||
|
gap: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-statuses > div {
|
||||||
|
background-color: #313131;
|
||||||
|
padding: 0.2rem 0 0.2rem 0.5em;
|
||||||
|
margin: 0.5em 0;
|
||||||
|
border-radius: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include smallScreen {
|
||||||
|
.item-top {
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -26,7 +26,8 @@
|
|||||||
<strong
|
<strong
|
||||||
v-if="apiStore.donatorsData.includes(timetable.driverName)"
|
v-if="apiStore.donatorsData.includes(timetable.driverName)"
|
||||||
class="text--donator"
|
class="text--donator"
|
||||||
:title="$t('donations.driver-message')"
|
data-tooltip-type="DonatorTooltip"
|
||||||
|
:data-tooltip-content="$t('donations.driver-message')"
|
||||||
>
|
>
|
||||||
{{ timetable.driverName }}
|
{{ timetable.driverName }}
|
||||||
</strong>
|
</strong>
|
||||||
|
|||||||
+1
-1
@@ -24,7 +24,7 @@ export namespace API {
|
|||||||
export type Response = Data[];
|
export type Response = Data[];
|
||||||
|
|
||||||
export interface Data {
|
export interface Data {
|
||||||
id: string;
|
id: number;
|
||||||
currentDuration: number;
|
currentDuration: number;
|
||||||
dispatcherId: number;
|
dispatcherId: number;
|
||||||
dispatcherName: string;
|
dispatcherName: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user