mirror of
https://github.com/Spythere/station-manager-2.0.git
synced 2026-05-04 22:18:15 +00:00
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"singleQuote": true,
|
||||||
|
"trailingComma": "all",
|
||||||
|
"endOfLine": "auto",
|
||||||
|
"printWidth": 120
|
||||||
|
}
|
||||||
Generated
-2259
File diff suppressed because it is too large
Load Diff
+2
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "station-manager-2.0",
|
"name": "station-manager-2.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "2.5.3",
|
"version": "2.6.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
@@ -10,6 +10,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.10.0",
|
"axios": "^1.10.0",
|
||||||
|
"lucide-vue-next": "^0.555.0",
|
||||||
"pinia": "^3.0.3",
|
"pinia": "^3.0.3",
|
||||||
"sass": "^1.89.2",
|
"sass": "^1.89.2",
|
||||||
"vue": "^3.5.17",
|
"vue": "^3.5.17",
|
||||||
|
|||||||
@@ -1,13 +1,20 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="bg" @click="closeRoutesModal"></div>
|
<div class="modal-bg" @click="closeRoutesModal"></div>
|
||||||
<div class="routes-modal" v-if="sceneriesStore.currentStation" @keydown.esc="closeRoutesModal" tabindex="0" ref="modal">
|
|
||||||
|
<div
|
||||||
|
v-if="sceneriesStore.currentStation"
|
||||||
|
class="modal routes-modal"
|
||||||
|
@keydown.esc="closeRoutesModal"
|
||||||
|
tabindex="0"
|
||||||
|
ref="modal"
|
||||||
|
>
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-wrapper">
|
<div class="modal-wrapper">
|
||||||
<h1>
|
<h1 class="modal-header">
|
||||||
Szlaki na scenerii {{ sceneriesStore.currentStation.name }}
|
Szlaki na scenerii {{ sceneriesStore.currentStation.name }}
|
||||||
<div class="exit" @click="closeRoutesModal">
|
<button class="modal-exit" @click="closeRoutesModal">
|
||||||
<img src="/icon-exit.svg" alt="exit icon" />
|
<LucideX :size="20" />
|
||||||
</div>
|
</button>
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<ul class="route-list">
|
<ul class="route-list">
|
||||||
@@ -16,19 +23,29 @@
|
|||||||
<div style="display: flex; justify-content: space-between; align-items: center; gap: 1em">
|
<div style="display: flex; justify-content: space-between; align-items: center; gap: 1em">
|
||||||
<span> Szlak: <input type="text" v-model="route.routeName" /> </span>
|
<span> Szlak: <input type="text" v-model="route.routeName" /> </span>
|
||||||
|
|
||||||
<button class="btn--icon">
|
<button class="route-delete-btn" @click="removeRoute(i)">
|
||||||
<img @click="removeRoute(i)" class="route-delete" src="/icon-trash.svg" alt="icon trash" />
|
<LucideTrash />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label :for="`${route.routeName}-internal`" style="display: inline-block">
|
<label :for="`${route.routeName}-internal`" style="display: inline-block">
|
||||||
<input type="checkbox" :name="`${route.routeName}-internal`" :id="`${route.routeName}-internal`" v-model="route.isInternal" />
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
:name="`${route.routeName}-internal`"
|
||||||
|
:id="`${route.routeName}-internal`"
|
||||||
|
v-model="route.isInternal"
|
||||||
|
/>
|
||||||
WEWNĘTRZNY
|
WEWNĘTRZNY
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label :for="`${route.routeName}-hidden`" style="display: inline-block">
|
<label :for="`${route.routeName}-hidden`" style="display: inline-block">
|
||||||
<input type="checkbox" :name="`${route.routeName}-hidden`" :id="`${route.routeName}-hidden`" v-model="route.hidden" />
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
:name="`${route.routeName}-hidden`"
|
||||||
|
:id="`${route.routeName}-hidden`"
|
||||||
|
v-model="route.hidden"
|
||||||
|
/>
|
||||||
UKRYTY
|
UKRYTY
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
@@ -36,12 +53,24 @@
|
|||||||
<div>
|
<div>
|
||||||
<b>Liczba torów:</b>
|
<b>Liczba torów:</b>
|
||||||
<label class="radio-choice">
|
<label class="radio-choice">
|
||||||
<input type="radio" :name="`${route.routeName}-tracks`" :value="1" :checked="route.routeTracks == 1" v-model="route.routeTracks" />
|
<input
|
||||||
|
type="radio"
|
||||||
|
:name="`${route.routeName}-tracks`"
|
||||||
|
:value="1"
|
||||||
|
:checked="route.routeTracks == 1"
|
||||||
|
v-model="route.routeTracks"
|
||||||
|
/>
|
||||||
<span>1</span>
|
<span>1</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label class="radio-choice">
|
<label class="radio-choice">
|
||||||
<input type="radio" :name="`${route.routeName}-tracks`" :value="2" :checked="route.routeTracks == 2" v-model="route.routeTracks" />
|
<input
|
||||||
|
type="radio"
|
||||||
|
:name="`${route.routeName}-tracks`"
|
||||||
|
:value="2"
|
||||||
|
:checked="route.routeTracks == 2"
|
||||||
|
v-model="route.routeTracks"
|
||||||
|
/>
|
||||||
<span>2</span>
|
<span>2</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
@@ -49,12 +78,24 @@
|
|||||||
<b>Elektryfikacja:</b>
|
<b>Elektryfikacja:</b>
|
||||||
|
|
||||||
<label class="radio-choice">
|
<label class="radio-choice">
|
||||||
<input type="radio" :name="`${route.routeName}-electr`" :value="true" :checked="route.isElectric" v-model="route.isElectric" />
|
<input
|
||||||
|
type="radio"
|
||||||
|
:name="`${route.routeName}-electr`"
|
||||||
|
:value="true"
|
||||||
|
:checked="route.isElectric"
|
||||||
|
v-model="route.isElectric"
|
||||||
|
/>
|
||||||
<span>Tak</span>
|
<span>Tak</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label class="radio-choice">
|
<label class="radio-choice">
|
||||||
<input type="radio" :name="`${route.routeName}-electr`" :value="false" :checked="!route.isElectric" v-model="route.isElectric" />
|
<input
|
||||||
|
type="radio"
|
||||||
|
:name="`${route.routeName}-electr`"
|
||||||
|
:value="false"
|
||||||
|
:checked="!route.isElectric"
|
||||||
|
v-model="route.isElectric"
|
||||||
|
/>
|
||||||
<span>Nie</span>
|
<span>Nie</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
@@ -86,8 +127,12 @@
|
|||||||
|
|
||||||
<div v-if="route.routeTracks == 1">Prędkość: <input type="number" v-model="route.routeSpeed" /> km/h</div>
|
<div v-if="route.routeTracks == 1">Prędkość: <input type="number" v-model="route.routeSpeed" /> km/h</div>
|
||||||
|
|
||||||
<div v-if="route.routeTracks == 2">Prędkość (wjazd na sc.): <input type="number" v-model="route.routeSpeed" /> km/h</div>
|
<div v-if="route.routeTracks == 2">
|
||||||
<div v-if="route.routeTracks == 2">Prędkość (wyjazd ze sc.): <input type="number" v-model="route.routeSpeedExit" /> km/h</div>
|
Prędkość (wjazd na sc.): <input type="number" v-model="route.routeSpeed" /> km/h
|
||||||
|
</div>
|
||||||
|
<div v-if="route.routeTracks == 2">
|
||||||
|
Prędkość (wyjazd ze sc.): <input type="number" v-model="route.routeSpeedExit" /> km/h
|
||||||
|
</div>
|
||||||
|
|
||||||
<div>Długość: <input type="number" v-model="route.routeLength" /> m</div>
|
<div>Długość: <input type="number" v-model="route.routeLength" /> m</div>
|
||||||
<div>Linia kolejowa: <input type="number" v-model="route.realLineNo" /></div>
|
<div>Linia kolejowa: <input type="number" v-model="route.realLineNo" /></div>
|
||||||
@@ -110,8 +155,11 @@ import { Ref, defineComponent, ref } from 'vue';
|
|||||||
import changeMixin from '../mixins/changeMixin';
|
import changeMixin from '../mixins/changeMixin';
|
||||||
import { SceneryRoutesInfo } from '../types/sceneries.types';
|
import { SceneryRoutesInfo } from '../types/sceneries.types';
|
||||||
import { useSceneriesStore } from '../stores/sceneries.store';
|
import { useSceneriesStore } from '../stores/sceneries.store';
|
||||||
|
import { LucideTrash, LucideX } from 'lucide-vue-next';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
components: { LucideX, LucideTrash },
|
||||||
|
|
||||||
setup() {
|
setup() {
|
||||||
const sceneriesStore = useSceneriesStore();
|
const sceneriesStore = useSceneriesStore();
|
||||||
const currentRoutes: Ref<SceneryRoutesInfo[]> = ref([]);
|
const currentRoutes: Ref<SceneryRoutesInfo[]> = ref([]);
|
||||||
@@ -164,9 +212,9 @@ export default defineComponent({
|
|||||||
const routeString = this.sceneriesStore.currentStation?.routesInfo
|
const routeString = this.sceneriesStore.currentStation?.routesInfo
|
||||||
.map(
|
.map(
|
||||||
(route) =>
|
(route) =>
|
||||||
`${route.isInternal ? '!' : ''}${route.routeName.trim()}_${route.routeTracks}${route.isElectric ? 'E' : 'N'}${
|
`${route.isInternal ? '!' : ''}${route.routeName.trim()}_${route.routeTracks}${
|
||||||
route.isRouteSBL ? 'S' : 'P'
|
route.isElectric ? 'E' : 'N'
|
||||||
}:${route.routeSpeed || 0}:${route.routeLength || 0}`
|
}${route.isRouteSBL ? 'S' : 'P'}:${route.routeSpeed || 0}:${route.routeLength || 0}`,
|
||||||
)
|
)
|
||||||
.join(';');
|
.join(';');
|
||||||
|
|
||||||
@@ -174,7 +222,9 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
|
|
||||||
saveRoutes() {
|
saveRoutes() {
|
||||||
const index = this.sceneriesStore.stationList.findIndex((station) => station.name === this.sceneriesStore.currentStation?.name);
|
const index = this.sceneriesStore.stationList.findIndex(
|
||||||
|
(station) => station.name === this.sceneriesStore.currentStation?.name,
|
||||||
|
);
|
||||||
|
|
||||||
if (index == -1) return;
|
if (index == -1) return;
|
||||||
|
|
||||||
@@ -186,45 +236,16 @@ export default defineComponent({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.bg {
|
@use '../styles/modal';
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100vw;
|
|
||||||
height: 100vh;
|
|
||||||
|
|
||||||
z-index: 100;
|
.routes-modal input {
|
||||||
|
display: inline;
|
||||||
|
margin: 0.25em 0;
|
||||||
|
|
||||||
background-color: #00000081;
|
color: black;
|
||||||
}
|
font-size: 1em;
|
||||||
|
|
||||||
.routes-modal {
|
max-width: 120px;
|
||||||
position: fixed;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
|
|
||||||
width: 95%;
|
|
||||||
max-width: 800px;
|
|
||||||
|
|
||||||
height: 95vh;
|
|
||||||
max-height: 1000px;
|
|
||||||
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
z-index: 101;
|
|
||||||
padding: 0.5em 0;
|
|
||||||
|
|
||||||
background-color: #333;
|
|
||||||
border-radius: 1rem;
|
|
||||||
|
|
||||||
input {
|
|
||||||
display: inline;
|
|
||||||
margin: 0.25em 0;
|
|
||||||
|
|
||||||
color: black;
|
|
||||||
font-size: 1em;
|
|
||||||
|
|
||||||
max-width: 120px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
label.radio-choice {
|
label.radio-choice {
|
||||||
@@ -256,35 +277,6 @@ label.radio-choice {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.exit {
|
|
||||||
img {
|
|
||||||
vertical-align: text-bottom;
|
|
||||||
width: 1.4em;
|
|
||||||
}
|
|
||||||
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
gap: 0.5em;
|
|
||||||
|
|
||||||
position: sticky;
|
|
||||||
top: -1px;
|
|
||||||
|
|
||||||
z-index: 100;
|
|
||||||
|
|
||||||
background-color: #333;
|
|
||||||
border-radius: 1rem 1rem 0 0;
|
|
||||||
padding: 0.25em 0.5em;
|
|
||||||
margin: 0;
|
|
||||||
|
|
||||||
font-size: 2em;
|
|
||||||
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-content {
|
.modal-content {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-rows: 1fr auto;
|
grid-template-rows: 1fr auto;
|
||||||
@@ -292,18 +284,14 @@ h1 {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-wrapper {
|
ul.route-list {
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.routes-modal ul {
|
|
||||||
list-style: none;
|
list-style: none;
|
||||||
padding: 0 1em;
|
padding: 0 1em;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
ul li {
|
ul.route-list li {
|
||||||
padding: 0.65em;
|
padding: 0.65em;
|
||||||
margin: 0.5em 0;
|
margin: 0.5em 0;
|
||||||
|
|
||||||
@@ -312,19 +300,12 @@ ul li {
|
|||||||
background-color: #222;
|
background-color: #222;
|
||||||
}
|
}
|
||||||
|
|
||||||
li > form {
|
ul.route-list li > form {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 0.25em;
|
gap: 0.25em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.route-delete {
|
|
||||||
margin: 0.5em;
|
|
||||||
width: 1.15em;
|
|
||||||
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.routes-actions {
|
.routes-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@@ -340,4 +321,13 @@ li > form {
|
|||||||
font-size: 1.2em;
|
font-size: 1.2em;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.route-delete-btn {
|
||||||
|
background-color: #444;
|
||||||
|
border-radius: 0.5em;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #555;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -0,0 +1,265 @@
|
|||||||
|
<template>
|
||||||
|
<div class="modal-bg" @click="closeModal()"></div>
|
||||||
|
|
||||||
|
<div class="modal" @keydown.esc="closeModal()" ref="modal" tabindex="0">
|
||||||
|
<div class="modal-content">
|
||||||
|
<h1 class="modal-header">
|
||||||
|
<span>{{ mode == 'update' ? 'Edytuj' : 'Dodaj' }} pojazd</span>
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<div class="modal-details" v-if="vehicleRef">
|
||||||
|
<div><b>ID:</b> {{ vehicleRef.id }}</div>
|
||||||
|
|
||||||
|
<div><b>Nazwa:</b> <input type="text" v-model="vehicleValues.name" /></div>
|
||||||
|
|
||||||
|
<div><b>Typ:</b> <input type="text" v-model="vehicleValues.type" /></div>
|
||||||
|
|
||||||
|
<div><b>Kabina lokomotywy:</b> <input type="text" v-model="vehicleValues.cabinName" /></div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<b>Grupa: </b>
|
||||||
|
<select id="select-group" ref="select-group" v-model="vehicleValues.vehicleGroupsId">
|
||||||
|
<option
|
||||||
|
v-for="value in vehiclesStore.vehicleGroupsTable"
|
||||||
|
:value="value.vehicleGroupRef.id"
|
||||||
|
:selected="vehicleRef.group.id == value.vehicleGroupRef.id"
|
||||||
|
>
|
||||||
|
{{ value.vehicleGroupRef.name }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label>Sponsorzy do: </label>
|
||||||
|
|
||||||
|
<input type="number" v-model="vehicleValues.restrictions!.sponsorOnly" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<span>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
name="vehicle-team-only"
|
||||||
|
id="vehicle-team-only"
|
||||||
|
v-model="vehicleValues.restrictions!.teamOnly"
|
||||||
|
/>
|
||||||
|
<label for="vehicle-team-only">Tylko dla zespołu</label>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span>
|
||||||
|
<input type="checkbox" name="vehicle-hidden" id="vehicle-hidden" v-model="vehicleValues.hidden" />
|
||||||
|
<label for="vehicle-hidden">Ukryty</label>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<b>Miniaturka:</b>
|
||||||
|
<div>
|
||||||
|
<img
|
||||||
|
:src="`https://stacjownik.spythere.eu/static/thumbnails/${vehicleRef.name}.png`"
|
||||||
|
height="60"
|
||||||
|
alt="thumbnail image"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<b>Podgląd - 300px:</b>
|
||||||
|
<div>
|
||||||
|
<img
|
||||||
|
:src="`https://stacjownik.spythere.eu/static/images/${vehicleRef.name}--300px.jpg`"
|
||||||
|
width="200"
|
||||||
|
alt="thumbnail"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<b>Podgląd - 800px:</b>
|
||||||
|
<div>
|
||||||
|
<img
|
||||||
|
:src="`https://stacjownik.spythere.eu/static/images/${vehicleRef.name}--800px.jpg`"
|
||||||
|
width="300"
|
||||||
|
alt="thumbnail"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-actions">
|
||||||
|
<button @click="updateVehicle">Aktualizuj dane</button>
|
||||||
|
<button @click="removeVehicle">Usuń pojazd</button>
|
||||||
|
<button @click="closeModal">Nie zapisuj</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, onMounted, PropType, reactive, ref, Ref, useTemplateRef, watch } from 'vue';
|
||||||
|
import { useVehiclesStore } from '../../stores/vehicles.store';
|
||||||
|
import { IVehicle, RemoveVehicleGroupAPIResponse, UpdateVehicleAPIResponse } from '../../types/vehicles.types';
|
||||||
|
import client from '../../common/http';
|
||||||
|
import { AxiosError } from 'axios';
|
||||||
|
|
||||||
|
const modalElementRef = useTemplateRef('modal');
|
||||||
|
|
||||||
|
const vehiclesStore = useVehiclesStore();
|
||||||
|
const currentVehicleRef: Ref<IVehicle | null> = ref(null);
|
||||||
|
|
||||||
|
let vehicleValues: Partial<IVehicle> = reactive({
|
||||||
|
name: '',
|
||||||
|
cabinName: '',
|
||||||
|
restrictions: {
|
||||||
|
teamOnly: false,
|
||||||
|
sponsorOnly: null,
|
||||||
|
},
|
||||||
|
type: '',
|
||||||
|
vehicleGroupsId: 0,
|
||||||
|
hidden: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
mode: {
|
||||||
|
type: String as PropType<'update' | 'create'>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const vehicleRef = computed(
|
||||||
|
() => vehiclesStore.vehiclesTable.find((v) => v.vehicleRef.id == vehiclesStore.selectedVehicleId)?.vehicleRef,
|
||||||
|
);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
currentVehicleRef.value =
|
||||||
|
vehiclesStore.vehiclesTable.find((v) => v.vehicleRef.id == vehiclesStore.selectedVehicleId)?.vehicleRef ?? null;
|
||||||
|
|
||||||
|
if (currentVehicleRef.value) {
|
||||||
|
vehicleValues.name = currentVehicleRef.value.name || '';
|
||||||
|
vehicleValues.cabinName = currentVehicleRef.value.cabinName || '';
|
||||||
|
vehicleValues.type = currentVehicleRef.value.type || '';
|
||||||
|
vehicleValues.vehicleGroupsId = currentVehicleRef.value.vehicleGroupsId || 0;
|
||||||
|
vehicleValues.hidden = currentVehicleRef.value.hidden;
|
||||||
|
vehicleValues.restrictions = {
|
||||||
|
sponsorOnly: currentVehicleRef.value.restrictions?.sponsorOnly ?? null,
|
||||||
|
teamOnly: currentVehicleRef.value.restrictions?.teamOnly ?? false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
modalElementRef.value?.focus();
|
||||||
|
});
|
||||||
|
|
||||||
|
function closeModal() {
|
||||||
|
vehiclesStore.selectedVehicleId = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleAPIErrors(error: unknown) {
|
||||||
|
console.error(error);
|
||||||
|
|
||||||
|
if (error instanceof AxiosError) {
|
||||||
|
return `Nie zaktualizowano pojazdu: ${error.response?.data.message}`;
|
||||||
|
} else {
|
||||||
|
return `Nie zaktualizowano pojazdu: ${error}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateVehicle() {
|
||||||
|
const vehicle = vehicleRef.value;
|
||||||
|
|
||||||
|
if (!vehicle) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const updatedData = (
|
||||||
|
await client.put<UpdateVehicleAPIResponse>(`/manager/vehicles/${vehicle.id}`, {
|
||||||
|
...vehicleValues,
|
||||||
|
restrictions: {
|
||||||
|
teamOnly: vehicleValues.restrictions?.teamOnly ?? false,
|
||||||
|
sponsorOnly: vehicleValues.restrictions?.sponsorOnly || null,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
).data;
|
||||||
|
|
||||||
|
const oldGroup = vehicle.group;
|
||||||
|
|
||||||
|
vehicle.name = updatedData.name;
|
||||||
|
vehicle.type = updatedData.type;
|
||||||
|
vehicle.vehicleGroupsId = updatedData.vehicleGroupsId;
|
||||||
|
vehicle.cabinName = updatedData.cabinName;
|
||||||
|
vehicle.hidden = updatedData.hidden;
|
||||||
|
vehicle.restrictions = updatedData.restrictions;
|
||||||
|
vehicle.group = vehiclesStore.vehicleGroupsTable.find(
|
||||||
|
(g) => g.vehicleGroupRef.id == updatedData.vehicleGroupsId,
|
||||||
|
)!.vehicleGroupRef;
|
||||||
|
|
||||||
|
const newGroup = vehicle.group;
|
||||||
|
|
||||||
|
// Updating group counts
|
||||||
|
oldGroup._count.vehicles -= 1;
|
||||||
|
newGroup._count.vehicles += 1;
|
||||||
|
|
||||||
|
alert('Zaktualizowano pojazd: ' + updatedData.name);
|
||||||
|
} catch (error) {
|
||||||
|
alert(handleAPIErrors(error));
|
||||||
|
}
|
||||||
|
|
||||||
|
vehiclesStore.selectedVehicleId = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function removeVehicle() {
|
||||||
|
const vehicle = vehicleRef.value;
|
||||||
|
|
||||||
|
if (!vehicle) return;
|
||||||
|
|
||||||
|
const removeConfirm = confirm('Czy na pewno chcesz usunąć ten pojazd?');
|
||||||
|
|
||||||
|
if (!removeConfirm) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const removedData = (await client.delete<RemoveVehicleGroupAPIResponse>(`/manager/vehicles/${vehicle.id}`)).data;
|
||||||
|
vehicle.group._count.vehicles -= 1;
|
||||||
|
|
||||||
|
vehiclesStore.vehiclesTable = vehiclesStore.vehiclesTable.filter((v) => v.vehicleRef.id != vehicle.id);
|
||||||
|
|
||||||
|
alert('Usunięto pojazd: ' + removedData.name);
|
||||||
|
} catch (error) {
|
||||||
|
alert(handleAPIErrors(error));
|
||||||
|
}
|
||||||
|
|
||||||
|
vehiclesStore.selectedVehicleId = -1;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@use '../../styles/modal';
|
||||||
|
|
||||||
|
.modal-content {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.5em;
|
||||||
|
grid-template-rows: auto 1fr auto;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-details {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5em;
|
||||||
|
overflow: auto;
|
||||||
|
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1.modal-header {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5em;
|
||||||
|
font-size: 1.1em;
|
||||||
|
padding: 1em;
|
||||||
|
|
||||||
|
button {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,326 @@
|
|||||||
|
<template>
|
||||||
|
<div class="modal-bg" @click="closeModal()"></div>
|
||||||
|
|
||||||
|
<div class="modal" @keydown.esc="closeModal()" ref="modal" tabindex="0">
|
||||||
|
<div class="modal-content">
|
||||||
|
<h1 class="modal-header">
|
||||||
|
<span>{{ mode == 'update' ? 'Edytuj' : 'Dodaj' }} grupę #{{ vehicleGroupRef?.id || '???' }}</span>
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<div class="modal-details" v-if="vehicleGroupRef">
|
||||||
|
<div><b>ID:</b> {{ vehicleGroupRef.id }}</div>
|
||||||
|
|
||||||
|
<div><b>Przypisane pojazdy:</b> {{ vehicleGroupRef._count.vehicles }}</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<b>Rodzaj pojazdów grupy: </b>
|
||||||
|
<select name="group-vehicle-type" id="group-vehicle-type" v-model="currentVehicleGroupType">
|
||||||
|
<option value="unit">Jednostka trakcyjna</option>
|
||||||
|
<option value="wagon">Wagon / ZT</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div><b>Nazwa: </b> <input type="text" v-model="vehicleGroupValues.name" /></div>
|
||||||
|
|
||||||
|
<div><b>Długość: </b> <input type="number" v-model="vehicleGroupValues.length" /> m</div>
|
||||||
|
|
||||||
|
<div><b>Masa: </b> <input type="number" v-model="vehicleGroupValues.weight" /> kg</div>
|
||||||
|
|
||||||
|
<div><b>Prędkość maks.:</b> <input type="number" v-model="vehicleGroupValues.speed" /> km/h</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<b>Prędkość luzem: </b>
|
||||||
|
<input type="number" v-model="vehicleGroupValues.speedLoco" :disabled="currentVehicleGroupType != 'unit'" />
|
||||||
|
km/h
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<b>Prędkość z ładunkiem (wagon): </b>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
v-model="vehicleGroupValues.speedLoaded"
|
||||||
|
:disabled="currentVehicleGroupType != 'wagon'"
|
||||||
|
/>
|
||||||
|
km/h
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<span>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
name="cold-start"
|
||||||
|
id="cold-start"
|
||||||
|
v-model="vehicleGroupValues.locoProps!.coldStart"
|
||||||
|
:disabled="currentVehicleGroupType != 'unit'"
|
||||||
|
/>
|
||||||
|
<label for="cold-start">Zimny start</label>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
name="double-manned"
|
||||||
|
id="double-manned"
|
||||||
|
v-model="vehicleGroupValues.locoProps!.doubleManned"
|
||||||
|
:disabled="currentVehicleGroupType != 'unit'"
|
||||||
|
/>
|
||||||
|
<label for="double-manned">Podwójna obsada</label>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="details-cargo-types" v-if="currentVehicleGroupType == 'wagon'">
|
||||||
|
<input type="checkbox" id="include-cargo-types" v-model="includeCargoTypes" />
|
||||||
|
<label for="include-cargo-types">Ładunki (wagon):</label>
|
||||||
|
<br />
|
||||||
|
<textarea name="" id="">{{ JSON.stringify(vehicleGroupValues.cargoTypes) }}</textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="details-mass-speeds">
|
||||||
|
<input type="checkbox" id="include-mass-speeds" v-model="includeMassSpeeds" />
|
||||||
|
<label for="include-mass-speeds">Dopuszczalne masy:</label>
|
||||||
|
(JSON: {{ isMassSpeedsJsonValid ? 'poprawny' : 'niepoprawny' }})
|
||||||
|
<br />
|
||||||
|
<textarea name="" id="" v-model="massSpeedsJsonString">{{
|
||||||
|
JSON.stringify(vehicleGroupValues.massSpeeds)
|
||||||
|
}}</textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else>Ups! Nie ma takiego elementu! :/</div>
|
||||||
|
|
||||||
|
<div class="modal-actions">
|
||||||
|
<button @click="updateVehicleGroup">Aktualizuj dane</button>
|
||||||
|
<button @click="removeVehicleGroup">Usuń grupę</button>
|
||||||
|
<button @click="closeModal">Nie zapisuj</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, onMounted, PropType, reactive, ref, Ref, useTemplateRef, watch } from 'vue';
|
||||||
|
import { useVehiclesStore } from '../../stores/vehicles.store';
|
||||||
|
import client from '../../common/http';
|
||||||
|
import { AxiosError } from 'axios';
|
||||||
|
import {
|
||||||
|
IVehicleGroup,
|
||||||
|
RemoveVehicleGroupAPIResponse,
|
||||||
|
UpdateVehicleGroupAPIResponse,
|
||||||
|
} from '../../types/vehicles.types';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
mode: {
|
||||||
|
type: String as PropType<'update' | 'create'>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const modalElementRef = useTemplateRef('modal');
|
||||||
|
|
||||||
|
const vehiclesStore = useVehiclesStore();
|
||||||
|
|
||||||
|
const currentVehicleGroupType = ref<'unit' | 'wagon'>('unit');
|
||||||
|
|
||||||
|
const includeMassSpeeds = ref(false);
|
||||||
|
const includeCargoTypes = ref(false);
|
||||||
|
|
||||||
|
const massSpeedsJsonString = ref('');
|
||||||
|
const cargoTypesJsonString = ref('');
|
||||||
|
|
||||||
|
const isMassSpeedsJsonValid = ref(true);
|
||||||
|
const isCargoTypesJsonValid = ref(true);
|
||||||
|
|
||||||
|
let vehicleGroupValues: Partial<IVehicleGroup> = reactive({
|
||||||
|
name: '',
|
||||||
|
length: 0,
|
||||||
|
weight: 0,
|
||||||
|
speed: 0,
|
||||||
|
speedLoaded: 0,
|
||||||
|
speedLoco: 0,
|
||||||
|
locoProps: {
|
||||||
|
coldStart: false,
|
||||||
|
doubleManned: false,
|
||||||
|
},
|
||||||
|
massSpeeds: null,
|
||||||
|
cargoTypes: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
const vehicleGroupRef = computed(
|
||||||
|
() =>
|
||||||
|
vehiclesStore.vehicleGroupsTable.find((g) => g.vehicleGroupRef.id == vehiclesStore.selectedVehicleGroupId)
|
||||||
|
?.vehicleGroupRef,
|
||||||
|
);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (vehicleGroupRef.value) {
|
||||||
|
populateVehicleGroupValues(vehicleGroupRef.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
modalElementRef.value?.focus();
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(massSpeedsJsonString, (val) => {
|
||||||
|
try {
|
||||||
|
JSON.parse(val);
|
||||||
|
isMassSpeedsJsonValid.value = true;
|
||||||
|
} catch (error) {
|
||||||
|
isMassSpeedsJsonValid.value = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function populateVehicleGroupValues(vehicleGroup: IVehicleGroup) {
|
||||||
|
if (!vehicleGroup.locoProps) currentVehicleGroupType.value = 'wagon';
|
||||||
|
|
||||||
|
vehicleGroupValues.name = vehicleGroup.name || '';
|
||||||
|
vehicleGroupValues.length = vehicleGroup.length || 0;
|
||||||
|
vehicleGroupValues.weight = vehicleGroup.weight || 0;
|
||||||
|
vehicleGroupValues.speed = vehicleGroup.speed || 0;
|
||||||
|
vehicleGroupValues.speedLoco = vehicleGroup.speedLoco || null;
|
||||||
|
vehicleGroupValues.speedLoaded = vehicleGroup.speedLoaded || null;
|
||||||
|
|
||||||
|
vehicleGroupValues.locoProps = {
|
||||||
|
coldStart: vehicleGroup.locoProps?.coldStart ?? false,
|
||||||
|
doubleManned: vehicleGroup.locoProps?.doubleManned ?? false,
|
||||||
|
};
|
||||||
|
|
||||||
|
vehicleGroupValues.cargoTypes = [];
|
||||||
|
|
||||||
|
vehicleGroupValues.massSpeeds = {
|
||||||
|
none: 0,
|
||||||
|
cargo: {},
|
||||||
|
passenger: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (vehicleGroup.cargoTypes) {
|
||||||
|
includeCargoTypes.value = true;
|
||||||
|
vehicleGroupValues.cargoTypes = vehicleGroup.cargoTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vehicleGroup.massSpeeds) {
|
||||||
|
includeMassSpeeds.value = true;
|
||||||
|
vehicleGroupValues.massSpeeds = vehicleGroup.massSpeeds;
|
||||||
|
}
|
||||||
|
|
||||||
|
massSpeedsJsonString.value = JSON.stringify(vehicleGroupValues.massSpeeds);
|
||||||
|
cargoTypesJsonString.value = JSON.stringify(vehicleGroupValues.cargoTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeModal() {
|
||||||
|
vehiclesStore.selectedVehicleGroupId = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleAPIErrors(error: unknown) {
|
||||||
|
console.error(error);
|
||||||
|
|
||||||
|
if (error instanceof AxiosError) {
|
||||||
|
return `Nie zaktualizowano grupy: ${error.response?.data.message}`;
|
||||||
|
} else {
|
||||||
|
return `Nie zaktualizowano grupy: ${error}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateVehicleGroup() {
|
||||||
|
const vehicleGroup = vehicleGroupRef.value;
|
||||||
|
|
||||||
|
if (!vehicleGroup) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const updatedData = (
|
||||||
|
await client.put<UpdateVehicleGroupAPIResponse>(`/manager/vehicleGroups/${vehicleGroup.id}`, {
|
||||||
|
name: vehicleGroupValues.name,
|
||||||
|
length: vehicleGroupValues.length,
|
||||||
|
weight: vehicleGroupValues.weight,
|
||||||
|
speed: vehicleGroupValues.speed,
|
||||||
|
speedLoaded: vehicleGroupValues.speedLoaded,
|
||||||
|
speedLoco: vehicleGroupValues.speedLoco,
|
||||||
|
locoProps: currentVehicleGroupType.value == 'unit' ? vehicleGroupValues.locoProps : null,
|
||||||
|
massSpeeds: includeMassSpeeds.value ? JSON.parse(massSpeedsJsonString.value) : null,
|
||||||
|
cargoTypes: includeCargoTypes.value ? JSON.parse(cargoTypesJsonString.value) : null,
|
||||||
|
} as Partial<IVehicleGroup>)
|
||||||
|
).data;
|
||||||
|
|
||||||
|
const tableObject = vehiclesStore.vehicleGroupsTable.find((g) => g.vehicleGroupRef.id == updatedData.id);
|
||||||
|
|
||||||
|
if (tableObject) {
|
||||||
|
tableObject.vehicleGroupRef = updatedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
// alert('Zaktualizowano grupę: ' + updatedData.name);
|
||||||
|
closeModal();
|
||||||
|
} catch (error) {
|
||||||
|
alert(handleAPIErrors(error));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function removeVehicleGroup() {
|
||||||
|
const vehicleGroup = vehicleGroupRef.value;
|
||||||
|
|
||||||
|
if (!vehicleGroup) return;
|
||||||
|
|
||||||
|
const removeConfirm = confirm('Czy na pewno chcesz usunąć ten pojazd?');
|
||||||
|
|
||||||
|
if (!removeConfirm) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const removedData = (
|
||||||
|
await client.delete<RemoveVehicleGroupAPIResponse>(`/manager/vehicleGroups/${vehicleGroup.id}`)
|
||||||
|
).data;
|
||||||
|
|
||||||
|
vehiclesStore.vehicleGroupsTable = vehiclesStore.vehicleGroupsTable.filter(
|
||||||
|
(v) => v.vehicleGroupRef.id != vehicleGroup.id,
|
||||||
|
);
|
||||||
|
|
||||||
|
// alert('Usunięto pojazd: ' + removedData.name);
|
||||||
|
closeModal();
|
||||||
|
} catch (error) {
|
||||||
|
alert(handleAPIErrors(error));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@use '../../styles/modal';
|
||||||
|
|
||||||
|
.modal-content {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.5em;
|
||||||
|
grid-template-rows: auto 1fr auto;
|
||||||
|
height: 100%;
|
||||||
|
line-height: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1.modal-header {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-details {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5em;
|
||||||
|
overflow: auto;
|
||||||
|
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details-cargo-types textarea,
|
||||||
|
.details-mass-speeds textarea {
|
||||||
|
width: 100%;
|
||||||
|
height: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cargo-types-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5em;
|
||||||
|
font-size: 1.1em;
|
||||||
|
padding: 1em;
|
||||||
|
|
||||||
|
button {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,199 @@
|
|||||||
|
<template>
|
||||||
|
<div class="table-search-box">
|
||||||
|
<input type="text" placeholder="Wyszukaj grupę..." v-model="vehicleGroupSearchInput" />
|
||||||
|
|
||||||
|
<button class="btn--center" @click="clearSearchInput()">
|
||||||
|
<LucideX :size="18" :stroke-width="4" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button class="btn--center" @click="addVehicleGroupRow()">
|
||||||
|
<LucidePlus :size="18" />
|
||||||
|
NOWA GRUPA
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="table-visible-results-box">
|
||||||
|
Pokazuj maks.
|
||||||
|
<input type="number" min="1" v-model="maxVisibleResults" />
|
||||||
|
wyników
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<VehicleGroupEditModal v-if="vehiclesStore.selectedVehicleGroupId != -1" mode="update" />
|
||||||
|
|
||||||
|
<div class="table-wrapper">
|
||||||
|
<table class="vehicle-manager-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<td
|
||||||
|
v-for="header in headers"
|
||||||
|
:style="`width: ${header.elementWidth}px`"
|
||||||
|
@click="sortTableBy(header.id)"
|
||||||
|
:data-sortable="header.sortable"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
{{ header.title }}
|
||||||
|
|
||||||
|
<LucideArrowUp :size="18" v-if="activeSortKey === header.id && activeSortDir == 1" />
|
||||||
|
<LucideArrowDown :size="18" v-else-if="activeSortKey === header.id" />
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
<tr
|
||||||
|
v-for="row in vehicleGroupsTableComp"
|
||||||
|
:key="row.vehicleGroupRef.id"
|
||||||
|
@click="openEditModal(row.vehicleGroupRef.id)"
|
||||||
|
>
|
||||||
|
<td>{{ row.vehicleGroupRef.id }}</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
{{ row.vehicleGroupRef.name }}
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
{{ row.vehicleGroupRef.length }}
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
{{ row.vehicleGroupRef.weight }}
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
{{ row.vehicleGroupRef.speed }}
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
{{ row.vehicleGroupRef.speedLoco }}
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
{{ row.vehicleGroupRef.speedLoaded }}
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
{{ row.vehicleGroupRef._count.vehicles }}
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
{{ row.vehicleGroupRef.locoProps?.coldStart ? '✅' : '❌' }}
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
{{ row.vehicleGroupRef.locoProps?.doubleManned ? '✅' : '❌' }}
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
{{ row.vehicleGroupRef.massSpeeds ? 'WPISANE' : 'BRAK' }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, onMounted, ref, watch } from 'vue';
|
||||||
|
import { useVehiclesStore } from '../../stores/vehicles.store';
|
||||||
|
import { LucideArrowDown, LucideArrowUp, LucidePlus, LucideX } from 'lucide-vue-next';
|
||||||
|
import { IVehicleGroup, IVehicleGroupTableRow, VehicleGroupEditRowKey } from '../../types/vehicles.types';
|
||||||
|
import VehicleGroupEditModal from './VehicleGroupEditModal.vue';
|
||||||
|
|
||||||
|
interface TableHeader {
|
||||||
|
id: string;
|
||||||
|
elementWidth: number;
|
||||||
|
title: string;
|
||||||
|
sortable: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sorterFunctions: Record<string, (v1: IVehicleGroup, v2: IVehicleGroup) => number> = {
|
||||||
|
id: (v1, v2) => (v1.id - v2.id) * activeSortDir.value,
|
||||||
|
name: (v1, v2) => v1.name.localeCompare(v2.name) * activeSortDir.value,
|
||||||
|
length: (v1, v2) => (v1.length - v2.length) * activeSortDir.value,
|
||||||
|
weight: (v1, v2) => (v1.weight - v2.weight) * activeSortDir.value,
|
||||||
|
speed: (v1, v2) => (v1.speed - v2.speed) * activeSortDir.value,
|
||||||
|
speedLoco: (v1, v2) => ((v1.speedLoco || 0) - (v2.speedLoco || 0)) * activeSortDir.value,
|
||||||
|
speedLoaded: (v1, v2) => ((v1.speedLoaded || 0) - (v2.speedLoaded || 0)) * activeSortDir.value,
|
||||||
|
};
|
||||||
|
|
||||||
|
const headers: TableHeader[] = [
|
||||||
|
{ id: 'id', elementWidth: 50, title: '#', sortable: true },
|
||||||
|
{ id: 'name', elementWidth: 150, title: 'Nazwa', sortable: true },
|
||||||
|
{ id: 'length', elementWidth: 100, title: 'Długość', sortable: true },
|
||||||
|
{ id: 'weight', elementWidth: 100, title: 'Masa', sortable: true },
|
||||||
|
{ id: 'speed', elementWidth: 100, title: 'Prędkość', sortable: true },
|
||||||
|
{ id: 'speedLoco', elementWidth: 100, title: 'Prędkość (lok)', sortable: true },
|
||||||
|
{ id: 'speedLoaded', elementWidth: 100, title: 'Prędkość (ład.)', sortable: true },
|
||||||
|
{ id: 'vehicles', elementWidth: 80, title: 'Pojazdy', sortable: true },
|
||||||
|
{ id: 'coldStart', elementWidth: 75, title: 'Zimny start', sortable: true },
|
||||||
|
{ id: 'doubleManned', elementWidth: 75, title: 'Podwójna obsada', sortable: true },
|
||||||
|
{ id: 'massSpeeds', elementWidth: 100, title: 'Prędkości wg masy', sortable: false },
|
||||||
|
];
|
||||||
|
|
||||||
|
const vehiclesStore = useVehiclesStore();
|
||||||
|
const vehicleGroupSearchInput = ref('');
|
||||||
|
|
||||||
|
const maxVisibleResults = ref(150);
|
||||||
|
const activeSortKey = ref<string>('id');
|
||||||
|
const activeSortDir = ref(1);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
maxVisibleResults.value =
|
||||||
|
Number(window.localStorage.getItem('maxVehicleGroupVisibleResults')) || maxVisibleResults.value;
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(maxVisibleResults, () => {
|
||||||
|
window.localStorage.setItem('maxVehicleGroupVisibleResults', maxVisibleResults.value.toString());
|
||||||
|
});
|
||||||
|
|
||||||
|
const vehicleGroupsTableComp = computed(() => {
|
||||||
|
return vehiclesStore.vehicleGroupsTable
|
||||||
|
.filter((row) =>
|
||||||
|
row.vehicleGroupRef.name.toLowerCase().startsWith(vehicleGroupSearchInput.value.trim().toLowerCase()),
|
||||||
|
)
|
||||||
|
.sort((r1, r2) => sorterFunctions[activeSortKey.value](r1.vehicleGroupRef, r2.vehicleGroupRef))
|
||||||
|
.filter((_, i) => i < maxVisibleResults.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
function clearSearchInput() {
|
||||||
|
vehicleGroupSearchInput.value = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function sortTableBy(id: string) {
|
||||||
|
if (!(id in sorterFunctions)) return;
|
||||||
|
|
||||||
|
if (activeSortKey.value == id) activeSortDir.value *= -1;
|
||||||
|
else activeSortDir.value = 1;
|
||||||
|
|
||||||
|
activeSortKey.value = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
function openEditModal(vehicleId: number) {
|
||||||
|
vehiclesStore.selectedVehicleGroupId = vehicleId;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function addVehicleGroupRow() {
|
||||||
|
const data = await vehiclesStore.createVehicleGroup({
|
||||||
|
name: 'VehicleGroup-' + Date.now(),
|
||||||
|
weight: 0,
|
||||||
|
length: 0,
|
||||||
|
speed: 0,
|
||||||
|
cargoTypes: null,
|
||||||
|
locoProps: null,
|
||||||
|
massSpeeds: null,
|
||||||
|
speedLoaded: null,
|
||||||
|
speedLoco: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
vehiclesStore.vehicleGroupsTable.push({
|
||||||
|
vehicleGroupRef: data,
|
||||||
|
});
|
||||||
|
|
||||||
|
vehicleGroupSearchInput.value = data.name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
@@ -0,0 +1,185 @@
|
|||||||
|
<template>
|
||||||
|
<div class="table-search-box">
|
||||||
|
<input type="text" placeholder="Wyszukaj pojazd..." v-model="vehicleSearchInput" />
|
||||||
|
|
||||||
|
<button class="btn--center" @click="clearSearchInput()">
|
||||||
|
<LucideX :size="18" :stroke-width="4" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button class="btn--center" @click="addVehicleRow()">
|
||||||
|
<LucidePlus :size="18" />
|
||||||
|
NOWY POJAZD
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="table-visible-results-box">
|
||||||
|
Pokazuj maks.
|
||||||
|
<input type="number" min="1" v-model="maxVisibleResults" />
|
||||||
|
wyników
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<VehicleEditModal v-if="vehiclesStore.selectedVehicleId != -1" mode="update" />
|
||||||
|
|
||||||
|
<div class="table-wrapper">
|
||||||
|
<table class="vehicle-manager-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<td
|
||||||
|
v-for="header in headers"
|
||||||
|
:style="`width: ${header.elementWidth}px`"
|
||||||
|
@click="sortTableBy(header.id)"
|
||||||
|
:data-sortable="header.sortable"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
{{ header.title }}
|
||||||
|
|
||||||
|
<LucideArrowUp :size="18" v-if="activeSortKey === header.id && activeSortDir == 1" />
|
||||||
|
<LucideArrowDown :size="18" v-else-if="activeSortKey === header.id" />
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="row in vehiclesTableComp" :key="row.vehicleRef.id" @click="openEditModal(row.vehicleRef.id)">
|
||||||
|
<td>{{ row.vehicleRef.id }}</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
{{ row.vehicleRef.name }}
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
{{ row.vehicleRef.type }}
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
{{ row.vehicleRef.cabinName }}
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
<span>{{ row.vehicleRef.group.name }}</span>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
<span v-if="row.vehicleRef.restrictions?.sponsorOnly">
|
||||||
|
{{ new Date(row.vehicleRef.restrictions.sponsorOnly).toLocaleString() }}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span v-else>❌</span>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
<span v-if="row.vehicleRef.restrictions?.teamOnly !== undefined">
|
||||||
|
{{ row.vehicleRef.restrictions.teamOnly ? '✅' : '❌' }}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span v-else>❌</span>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
{{ row.vehicleRef.hidden ? '✅' : '❌' }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, onMounted, ref, watch } from 'vue';
|
||||||
|
import { useVehiclesStore } from '../../stores/vehicles.store';
|
||||||
|
import { IVehicle } from '../../types/vehicles.types';
|
||||||
|
import { LucideArrowDown, LucideArrowUp, LucidePlus, LucideX } from 'lucide-vue-next';
|
||||||
|
import VehicleEditModal from './VehicleEditModal.vue';
|
||||||
|
|
||||||
|
interface TableHeader {
|
||||||
|
id: string;
|
||||||
|
elementWidth: number;
|
||||||
|
title: string;
|
||||||
|
sortable: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sorterFunctions: Record<string, (v1: IVehicle, v2: IVehicle) => number> = {
|
||||||
|
id: (v1, v2) => (v1.id - v2.id) * activeSortDir.value,
|
||||||
|
name: (v1, v2) => v1.name.localeCompare(v2.name) * activeSortDir.value,
|
||||||
|
cabinName: (v1, v2) => (v1.cabinName || '').localeCompare(v2.cabinName || '') * activeSortDir.value,
|
||||||
|
type: (v1, v2) => v1.type.localeCompare(v2.type) * activeSortDir.value,
|
||||||
|
group: (v1, v2) => (v1.group.id - v2.group.id) * activeSortDir.value,
|
||||||
|
sponsorOnly: (v1, v2) =>
|
||||||
|
((v1.restrictions?.sponsorOnly || v1.id) - (v2.restrictions?.sponsorOnly || v2.id)) * activeSortDir.value,
|
||||||
|
teamOnly: (v1, v2) =>
|
||||||
|
((v1.restrictions?.teamOnly ? 1 : v1.id) - (v2.restrictions?.teamOnly ? 1 : v2.id)) * activeSortDir.value,
|
||||||
|
hidden: (v1, v2) => (Number(v1.hidden) || v1.id) - (Number(v2.hidden) || v2.id) * activeSortDir.value,
|
||||||
|
};
|
||||||
|
|
||||||
|
const headers: TableHeader[] = [
|
||||||
|
{ id: 'id', elementWidth: 50, title: '#', sortable: true },
|
||||||
|
{ id: 'name', elementWidth: 200, title: 'Nazwa', sortable: true },
|
||||||
|
{ id: 'type', elementWidth: 150, title: 'Typ', sortable: true },
|
||||||
|
{ id: 'cabinName', elementWidth: 150, title: 'Kabina (lok.)', sortable: true },
|
||||||
|
{ id: 'group', elementWidth: 170, title: 'Grupa', sortable: true },
|
||||||
|
{ id: 'sponsorOnly', elementWidth: 130, title: 'Tylko sponsorzy do', sortable: true },
|
||||||
|
{ id: 'teamOnly', elementWidth: 100, title: 'Tylko zespół', sortable: true },
|
||||||
|
{ id: 'hidden', elementWidth: 75, title: 'Ukryty', sortable: true },
|
||||||
|
];
|
||||||
|
|
||||||
|
const vehiclesStore = useVehiclesStore();
|
||||||
|
|
||||||
|
const vehicleSearchInput = ref('');
|
||||||
|
const maxVisibleResults = ref(150);
|
||||||
|
const activeSortKey = ref<string>('id');
|
||||||
|
const activeSortDir = ref(1);
|
||||||
|
|
||||||
|
const vehiclesTableComp = computed(() => {
|
||||||
|
return vehiclesStore.vehiclesTable
|
||||||
|
.filter((row) => row.vehicleRef.name.toLowerCase().includes(vehicleSearchInput.value.trim().toLowerCase()))
|
||||||
|
.sort((v1, v2) => sorterFunctions[activeSortKey.value](v1.vehicleRef, v2.vehicleRef))
|
||||||
|
.filter((_, i) => i < maxVisibleResults.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
maxVisibleResults.value = Number(window.localStorage.getItem('maxVehicleVisibleResults')) || maxVisibleResults.value;
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(maxVisibleResults, () => {
|
||||||
|
window.localStorage.setItem('maxVehicleVisibleResults', maxVisibleResults.value.toString());
|
||||||
|
});
|
||||||
|
|
||||||
|
function clearSearchInput() {
|
||||||
|
vehicleSearchInput.value = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function sortTableBy(id: string) {
|
||||||
|
if (!(id in sorterFunctions)) return;
|
||||||
|
|
||||||
|
if (activeSortKey.value == id) activeSortDir.value *= -1;
|
||||||
|
else activeSortDir.value = 1;
|
||||||
|
|
||||||
|
activeSortKey.value = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
function openEditModal(vehicleId: number) {
|
||||||
|
vehiclesStore.selectedVehicleId = vehicleId;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function addVehicleRow() {
|
||||||
|
const createdVehicleData = await vehiclesStore.createVehicle({
|
||||||
|
name: 'Vehicle-' + Date.now(),
|
||||||
|
type: 'loco-electric',
|
||||||
|
hidden: true,
|
||||||
|
vehicleGroupsId: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (createdVehicleData) {
|
||||||
|
vehiclesStore.vehiclesTable.push({
|
||||||
|
vehicleRef: {
|
||||||
|
...createdVehicleData,
|
||||||
|
group: vehiclesStore.vehicleGroupsTable.find((g) => g.vehicleGroupRef.id == createdVehicleData.vehicleGroupsId)!
|
||||||
|
.vehicleGroupRef,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
vehiclesStore.selectedVehicleId = createdVehicleData.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -1,6 +1,21 @@
|
|||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
import client from '../common/http';
|
import client from '../common/http';
|
||||||
import { IVehicle, IVehicleTableRow, VehicleAPIResponse } from '../types/vehicles.types';
|
import {
|
||||||
|
ICreateVehicleBody,
|
||||||
|
IVehicle,
|
||||||
|
IVehicleAPI,
|
||||||
|
IVehicleGroupTableRow,
|
||||||
|
IVehicleTableRow,
|
||||||
|
GetVehiclesAPIResponse,
|
||||||
|
GetVehicleGroupsAPIResponse,
|
||||||
|
UpdateVehicleAPIResponse,
|
||||||
|
CreateVehicleAPIResponse,
|
||||||
|
RemoveVehicleAPIResponse,
|
||||||
|
CreateVehicleGroupAPIResponse,
|
||||||
|
ICreateVehicleGroupBody,
|
||||||
|
RemoveVehicleGroupAPIResponse,
|
||||||
|
IVehicleGroupAPI,
|
||||||
|
} from '../types/vehicles.types';
|
||||||
import { LoadingState } from '../types/common.types';
|
import { LoadingState } from '../types/common.types';
|
||||||
|
|
||||||
export const useVehiclesStore = defineStore('vehiclesStore', {
|
export const useVehiclesStore = defineStore('vehiclesStore', {
|
||||||
@@ -8,7 +23,11 @@ export const useVehiclesStore = defineStore('vehiclesStore', {
|
|||||||
vehicles: [] as IVehicle[],
|
vehicles: [] as IVehicle[],
|
||||||
dataState: LoadingState.INIT,
|
dataState: LoadingState.INIT,
|
||||||
|
|
||||||
|
selectedVehicleId: -1,
|
||||||
|
selectedVehicleGroupId: -1,
|
||||||
|
|
||||||
vehiclesTable: [] as IVehicleTableRow[],
|
vehiclesTable: [] as IVehicleTableRow[],
|
||||||
|
vehicleGroupsTable: [] as IVehicleGroupTableRow[],
|
||||||
}),
|
}),
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
@@ -16,9 +35,25 @@ export const useVehiclesStore = defineStore('vehiclesStore', {
|
|||||||
try {
|
try {
|
||||||
this.dataState = LoadingState.LOADING;
|
this.dataState = LoadingState.LOADING;
|
||||||
|
|
||||||
const vehiclesResponseData = (await client.get<VehicleAPIResponse>(`api/getVehicles`)).data;
|
const [vehiclesData, vehicleGroupsData] = await Promise.all([
|
||||||
this.vehicles = vehiclesResponseData;
|
(await client.get<GetVehiclesAPIResponse>(`manager/vehicles`)).data,
|
||||||
this.vehiclesTable = vehiclesResponseData.map((v) => ({ vehicleRef: v, pendingChanges: {} }));
|
(await client.get<GetVehicleGroupsAPIResponse>(`manager/vehicleGroups`)).data,
|
||||||
|
]);
|
||||||
|
|
||||||
|
this.vehicles = vehiclesData.map((v) => {
|
||||||
|
return {
|
||||||
|
...v,
|
||||||
|
group: vehicleGroupsData.find((g) => g.id == v.vehicleGroupsId)!,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
this.vehiclesTable = this.vehicles.map((v) => ({
|
||||||
|
vehicleRef: v,
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.vehicleGroupsTable = vehicleGroupsData.map((g) => ({
|
||||||
|
vehicleGroupRef: g,
|
||||||
|
}));
|
||||||
|
|
||||||
this.dataState = LoadingState.SUCCESS;
|
this.dataState = LoadingState.SUCCESS;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -26,5 +61,74 @@ export const useVehiclesStore = defineStore('vehiclesStore', {
|
|||||||
this.dataState = LoadingState.ERROR;
|
this.dataState = LoadingState.ERROR;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async updateVehicleGroup(vehicleId: number, propKey: string, propValue: any) {
|
||||||
|
try {
|
||||||
|
const response = await client.put<IVehicleGroupAPI>(`/manager/vehicleGroups/${vehicleId}`, {
|
||||||
|
[propKey]: propValue,
|
||||||
|
});
|
||||||
|
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
|
async createVehicle(vehicleRowData: ICreateVehicleBody) {
|
||||||
|
try {
|
||||||
|
const response = await client.post<CreateVehicleAPIResponse>(`/manager/vehicles`, {
|
||||||
|
name: vehicleRowData.name,
|
||||||
|
type: vehicleRowData.type,
|
||||||
|
vehicleGroupsId: vehicleRowData.vehicleGroupsId,
|
||||||
|
hidden: vehicleRowData.hidden,
|
||||||
|
simulatorVersion: '2025.3.2',
|
||||||
|
});
|
||||||
|
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
|
async createVehicleGroup(vehicleRowData: ICreateVehicleGroupBody) {
|
||||||
|
try {
|
||||||
|
const response = await client.post<CreateVehicleGroupAPIResponse, any, ICreateVehicleGroupBody>(
|
||||||
|
`/manager/vehicleGroups`,
|
||||||
|
{
|
||||||
|
name: vehicleRowData.name,
|
||||||
|
weight: vehicleRowData.weight,
|
||||||
|
length: vehicleRowData.length,
|
||||||
|
speed: vehicleRowData.speed,
|
||||||
|
speedLoaded: vehicleRowData.speedLoaded,
|
||||||
|
speedLoco: vehicleRowData.speedLoco,
|
||||||
|
massSpeeds: vehicleRowData.massSpeeds,
|
||||||
|
cargoTypes: vehicleRowData.cargoTypes,
|
||||||
|
locoProps: vehicleRowData.locoProps,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
|
async removeVehicle(vehicleId: number) {
|
||||||
|
try {
|
||||||
|
const response = await client.delete<RemoveVehicleAPIResponse>(`/manager/vehicles/${vehicleId}`);
|
||||||
|
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -33,6 +33,60 @@ a:visited {
|
|||||||
box-sizing: inherit;
|
box-sizing: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tables
|
||||||
|
table {
|
||||||
|
table-layout: fixed;
|
||||||
|
text-align: center;
|
||||||
|
color: white;
|
||||||
|
position: relative;
|
||||||
|
border-collapse: collapse;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
table thead {
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
table thead tr td {
|
||||||
|
padding: 0.4rem 0.45rem;
|
||||||
|
background-color: #151b24;
|
||||||
|
color: white;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
table thead tr td img {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
table tbody tr {
|
||||||
|
background-color: #2c394b;
|
||||||
|
transition: background-color 100ms;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
table tr:nth-child(even) {
|
||||||
|
background-color: #334756;
|
||||||
|
}
|
||||||
|
|
||||||
|
table tr:hover {
|
||||||
|
background-color: #1a293b;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
table tbody tr td {
|
||||||
|
padding: 0.3rem 0.5rem;
|
||||||
|
border: 1px solid #2c2c2c;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
table tbody tr td img {
|
||||||
|
height: 1.45em;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Other
|
||||||
button,
|
button,
|
||||||
select,
|
select,
|
||||||
input {
|
input {
|
||||||
@@ -75,6 +129,12 @@ button {
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.btn--center {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
&:focus-visible {
|
&:focus-visible {
|
||||||
outline: 1px solid gold;
|
outline: 1px solid gold;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
.modal-bg {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
|
||||||
|
z-index: 100;
|
||||||
|
|
||||||
|
background-color: #00000081;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal {
|
||||||
|
position: fixed;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
|
||||||
|
width: 95%;
|
||||||
|
max-width: 800px;
|
||||||
|
|
||||||
|
height: 95vh;
|
||||||
|
max-height: 1000px;
|
||||||
|
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
z-index: 101;
|
||||||
|
padding: 0.5em;
|
||||||
|
|
||||||
|
background-color: #333;
|
||||||
|
border-radius: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-wrapper {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.modal-exit {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0;
|
||||||
|
background-color: #444;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
width: 1.5em;
|
||||||
|
height: 1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h1.modal-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 0.5em;
|
||||||
|
|
||||||
|
position: sticky;
|
||||||
|
top: -1px;
|
||||||
|
|
||||||
|
z-index: 100;
|
||||||
|
|
||||||
|
background-color: #333;
|
||||||
|
border-radius: 1rem 1rem 0 0;
|
||||||
|
padding: 0.25em 0.5em;
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
font-size: 2em;
|
||||||
|
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
+85
-26
@@ -1,53 +1,112 @@
|
|||||||
export type VehicleAPIResponse = IVehicle[];
|
export type GetVehiclesAPIResponse = IVehicleAPI[];
|
||||||
|
export type UpdateVehicleAPIResponse = IVehicleAPI;
|
||||||
|
export type CreateVehicleAPIResponse = IVehicleAPI;
|
||||||
|
export type RemoveVehicleAPIResponse = IVehicleAPI;
|
||||||
|
|
||||||
export interface IVehicle {
|
export type GetVehicleGroupsAPIResponse = IVehicleGroup[];
|
||||||
|
export type UpdateVehicleGroupAPIResponse = IVehicleGroup;
|
||||||
|
export type CreateVehicleGroupAPIResponse = IVehicleGroup;
|
||||||
|
export type RemoveVehicleGroupAPIResponse = IVehicleGroup;
|
||||||
|
|
||||||
|
export interface IVehicleAPI {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
type: string;
|
type: string;
|
||||||
cabinName?: string;
|
cabinName?: string;
|
||||||
restrictions?: IVehicleRestrictions;
|
restrictions?: IVehicleRestrictions;
|
||||||
vehicleGroupsId: number;
|
vehicleGroupsId: number;
|
||||||
|
hidden: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IVehicle extends IVehicleAPI {
|
||||||
group: IVehicleGroup;
|
group: IVehicleGroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IVehicleRestrictions {
|
export interface ICreateVehicleBody {
|
||||||
sponsorOnly?: number;
|
|
||||||
teamOnly?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IVehicleGroup {
|
|
||||||
id: number;
|
|
||||||
name: string;
|
name: string;
|
||||||
speed: number;
|
type: string;
|
||||||
length: number;
|
cabinName?: string;
|
||||||
weight: number;
|
restrictions?: IVehicleRestrictions;
|
||||||
cargoTypes?: IVehicleCargoType[];
|
vehicleGroupsId: number;
|
||||||
locoProps?: IVehicleLocoProps;
|
hidden: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IVehicleCargoType {
|
export interface IVehicleRestrictions {
|
||||||
id: string;
|
sponsorOnly?: number | null;
|
||||||
weight: number;
|
teamOnly?: boolean;
|
||||||
}
|
|
||||||
|
|
||||||
export interface IVehicleLocoProps {
|
|
||||||
coldStart: boolean;
|
|
||||||
doubleManned: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IVehicleTableRow {
|
export interface IVehicleTableRow {
|
||||||
vehicleRef: IVehicle;
|
vehicleRef: IVehicle;
|
||||||
pendingChanges: Partial<IVehicle>;
|
// pendingChanges: Partial<IVehicle>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum VehicleEditRowKey {
|
export enum VehicleEditRowKey {
|
||||||
NAME = 'name',
|
NAME = 'name',
|
||||||
TYPE = 'type',
|
TYPE = 'type',
|
||||||
CABIN = 'cabinName',
|
CABIN = 'cabinName',
|
||||||
|
HIDDEN = 'hidden',
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum VehicleEditRestrictionKey {
|
export enum VehicleEditRestrictionKey {
|
||||||
SPONSOR_ONLY = "sponsorOnly",
|
SPONSOR_ONLY = 'sponsorOnly',
|
||||||
TEAM_ONLY = "teamOnly"
|
TEAM_ONLY = 'teamOnly',
|
||||||
|
}
|
||||||
|
|
||||||
|
// VEHICLE GROUPS
|
||||||
|
export interface IVehicleGroupMassSpeeds {
|
||||||
|
none: number;
|
||||||
|
passenger: Record<number, number> | null;
|
||||||
|
cargo: Record<number, number> | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IVehicleGroupCargoType {
|
||||||
|
id: string;
|
||||||
|
weight: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IVehicleGroupLocoProps {
|
||||||
|
coldStart: boolean;
|
||||||
|
doubleManned: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IVehicleGroupAPI {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
speed: number;
|
||||||
|
speedLoaded: number | null;
|
||||||
|
speedLoco: number | null;
|
||||||
|
length: number;
|
||||||
|
weight: number;
|
||||||
|
cargoTypes: IVehicleGroupCargoType[] | null;
|
||||||
|
locoProps: IVehicleGroupLocoProps | null;
|
||||||
|
massSpeeds: IVehicleGroupMassSpeeds | null;
|
||||||
|
_count: { vehicles: number };
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IVehicleGroupTableRow {
|
||||||
|
vehicleGroupRef: IVehicleGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IVehicleGroup extends IVehicleGroupAPI {}
|
||||||
|
|
||||||
|
export interface ICreateVehicleGroupBody {
|
||||||
|
name: string;
|
||||||
|
speed: number;
|
||||||
|
speedLoaded: number | null;
|
||||||
|
speedLoco: number | null;
|
||||||
|
length: number;
|
||||||
|
weight: number;
|
||||||
|
cargoTypes: IVehicleGroupCargoType[] | null;
|
||||||
|
locoProps: IVehicleGroupLocoProps | null;
|
||||||
|
massSpeeds: IVehicleGroupMassSpeeds | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum VehicleGroupEditRowKey {
|
||||||
|
NAME = 'name',
|
||||||
|
SPEED = 'speed',
|
||||||
|
SPEED_LOADED = 'speedLoaded',
|
||||||
|
SPEED_LOCO = 'speedLoco',
|
||||||
|
LENGTH = 'length',
|
||||||
|
WEIGHT = 'weight',
|
||||||
}
|
}
|
||||||
+20
-54
@@ -19,9 +19,15 @@
|
|||||||
|
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="(station, row) in sceneriesStore.sortedStationList" tabindex="0">
|
<tr v-for="(station, row) in sceneriesStore.sortedStationList" tabindex="0">
|
||||||
<td v-for="{ id: propName } in headers" @click="changeProperty(station, row, propName as string)" :class="propName">
|
<td
|
||||||
|
v-for="{ id: propName } in headers"
|
||||||
|
@click="changeProperty(station, row, propName as string)"
|
||||||
|
:class="propName"
|
||||||
|
>
|
||||||
<span v-if="propName === 'url'" :style="station.url ? 'color: gold' : 'color: gray;'">URL</span>
|
<span v-if="propName === 'url'" :style="station.url ? 'color: gold' : 'color: gray;'">URL</span>
|
||||||
<span v-else-if="propName === 'projectUrl'" :style="station.projectUrl ? 'color: gold' : 'color: gray;'">URL</span>
|
<span v-else-if="propName === 'projectUrl'" :style="station.projectUrl ? 'color: gold' : 'color: gray;'"
|
||||||
|
>URL</span
|
||||||
|
>
|
||||||
|
|
||||||
<span v-else-if="propName === 'checkpoints'">{{ station[propName] ? 'POKAŻ' : 'DODAJ' }}</span>
|
<span v-else-if="propName === 'checkpoints'">{{ station[propName] ? 'POKAŻ' : 'DODAJ' }}</span>
|
||||||
|
|
||||||
@@ -141,7 +147,9 @@ export default defineComponent({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const stationListRow = this.sceneriesStore.stationList.findIndex((station) => station.name == this.sceneriesStore.sortedStationList[row].name);
|
const stationListRow = this.sceneriesStore.stationList.findIndex(
|
||||||
|
(station) => station.name == this.sceneriesStore.sortedStationList[row].name,
|
||||||
|
);
|
||||||
|
|
||||||
if (stationListRow == -1) return;
|
if (stationListRow == -1) return;
|
||||||
const oldValue = (this.sceneriesStore.stationList[stationListRow] as any)[propertyName];
|
const oldValue = (this.sceneriesStore.stationList[stationListRow] as any)[propertyName];
|
||||||
@@ -152,15 +160,21 @@ export default defineComponent({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let newValue = prompt(`Zmień wartość dla rubryki ${this.headers.find((h) => h.id === propertyName)?.value ?? ''}`, oldValue || '');
|
let newValue = prompt(
|
||||||
|
`Zmień wartość dla rubryki ${this.headers.find((h) => h.id === propertyName)?.value ?? ''}`,
|
||||||
|
oldValue || '',
|
||||||
|
);
|
||||||
if (newValue == null) return;
|
if (newValue == null) return;
|
||||||
(this.sceneriesStore.stationList[stationListRow] as any)[propertyName] = typeof oldValue === 'number' ? parseInt(newValue) : newValue;
|
(this.sceneriesStore.stationList[stationListRow] as any)[propertyName] =
|
||||||
|
typeof oldValue === 'number' ? parseInt(newValue) : newValue;
|
||||||
// this.$set(this.stationList[stationListRow], propertyName, parseInt(newValue));
|
// this.$set(this.stationList[stationListRow], propertyName, parseInt(newValue));
|
||||||
this.addChange(station, propertyName, oldValue, typeof oldValue === 'number' ? parseInt(newValue) : newValue);
|
this.addChange(station, propertyName, oldValue, typeof oldValue === 'number' ? parseInt(newValue) : newValue);
|
||||||
},
|
},
|
||||||
|
|
||||||
changeCheckpoints(row: number) {
|
changeCheckpoints(row: number) {
|
||||||
const stationListRow = this.sceneriesStore.stationList.findIndex((station) => station.name == this.sceneriesStore.sortedStationList[row].name);
|
const stationListRow = this.sceneriesStore.stationList.findIndex(
|
||||||
|
(station) => station.name == this.sceneriesStore.sortedStationList[row].name,
|
||||||
|
);
|
||||||
|
|
||||||
if (stationListRow == -1) return;
|
if (stationListRow == -1) return;
|
||||||
const oldCheckpoints = this.sceneriesStore.stationList[stationListRow].checkpoints;
|
const oldCheckpoints = this.sceneriesStore.stationList[stationListRow].checkpoints;
|
||||||
@@ -200,33 +214,6 @@ export default defineComponent({
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
table {
|
|
||||||
table-layout: fixed;
|
|
||||||
text-align: center;
|
|
||||||
color: white;
|
|
||||||
position: relative;
|
|
||||||
border-collapse: collapse;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
table thead {
|
|
||||||
position: sticky;
|
|
||||||
top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
table thead td {
|
|
||||||
padding: 0.4rem 0.45rem;
|
|
||||||
background-color: #151b24;
|
|
||||||
color: white;
|
|
||||||
top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
table tbody tr {
|
|
||||||
background-color: #2c394b;
|
|
||||||
transition: background-color 100ms;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
table tr.current {
|
table tr.current {
|
||||||
outline: 1px solid white;
|
outline: 1px solid white;
|
||||||
}
|
}
|
||||||
@@ -235,25 +222,4 @@ table td.routes {
|
|||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
min-width: 150px;
|
min-width: 150px;
|
||||||
}
|
}
|
||||||
|
|
||||||
table tr:nth-child(even) {
|
|
||||||
background-color: #334756;
|
|
||||||
}
|
|
||||||
|
|
||||||
table tr:hover {
|
|
||||||
background-color: #1a293b;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
table tbody tr td {
|
|
||||||
padding: 0.3rem 0.5rem;
|
|
||||||
border: 1px solid #2c2c2c;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
td img {
|
|
||||||
height: 1.45em;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -6,188 +6,47 @@
|
|||||||
<img class="brand-image" src="/icon-logo.svg" alt="logo" />
|
<img class="brand-image" src="/icon-logo.svg" alt="logo" />
|
||||||
<h3>Edytor pojazdów</h3>
|
<h3>Edytor pojazdów</h3>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="search-box">
|
|
||||||
<input type="text" placeholder="Wyszukaj pojazd..." v-model="vehicleSearchValue" />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="table-wrapper">
|
<div class="mode-change-box">
|
||||||
<table>
|
<button @click="changeTab('vehicles')">POJAZDY</button>
|
||||||
<thead>
|
<button @click="changeTab('vehicleGroups')">GRUPY</button>
|
||||||
<tr>
|
|
||||||
<th style="width: 50px">#</th>
|
|
||||||
<th style="width: 300px">Nazwa</th>
|
|
||||||
<th style="width: 200px">Typ</th>
|
|
||||||
<th style="width: 150px">Kabina (lok.)</th>
|
|
||||||
<th style="width: 150px">Grupa</th>
|
|
||||||
<th style="width: 150px">Tylko sponsorzy do</th>
|
|
||||||
<th style="width: 150px">Tylko zespół</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
|
|
||||||
<tbody>
|
|
||||||
<tr v-for="row in vehiclesTableComp" :key="row.vehicleRef.id">
|
|
||||||
<td>{{ row.vehicleRef.id }}</td>
|
|
||||||
<td
|
|
||||||
class="editable"
|
|
||||||
:data-pending="row.pendingChanges.name !== undefined && row.pendingChanges.name !== row.vehicleRef.name"
|
|
||||||
@click="editRowPrimitive(row, VehicleEditRowKey.NAME)"
|
|
||||||
>
|
|
||||||
{{ row.pendingChanges.name ?? row.vehicleRef.name }}
|
|
||||||
</td>
|
|
||||||
<td
|
|
||||||
class="editable"
|
|
||||||
:data-pending="row.pendingChanges.type !== undefined && row.pendingChanges.type !== row.vehicleRef.type"
|
|
||||||
@click="editRowPrimitive(row, VehicleEditRowKey.TYPE)"
|
|
||||||
>
|
|
||||||
{{ row.pendingChanges.type ?? row.vehicleRef.type }}
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td
|
|
||||||
class="editable"
|
|
||||||
:data-pending="row.pendingChanges.cabinName !== undefined && row.pendingChanges.cabinName !== row.vehicleRef.cabinName"
|
|
||||||
@click="editRowPrimitive(row, VehicleEditRowKey.CABIN)"
|
|
||||||
>
|
|
||||||
{{ row.pendingChanges.cabinName ?? row.vehicleRef.cabinName }}
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td class="editable">{{ row.vehicleRef.group.name }}</td>
|
|
||||||
<td
|
|
||||||
class="editable"
|
|
||||||
:data-pending="
|
|
||||||
row.pendingChanges.restrictions?.sponsorOnly !== undefined &&
|
|
||||||
row.pendingChanges.restrictions.sponsorOnly !== row.vehicleRef.restrictions?.sponsorOnly
|
|
||||||
"
|
|
||||||
@click="editRowRestrictions(row, VehicleEditRestrictionKey.SPONSOR_ONLY)"
|
|
||||||
>
|
|
||||||
<span v-if="row.pendingChanges.restrictions?.sponsorOnly !== undefined">
|
|
||||||
{{ new Date(row.pendingChanges.restrictions.sponsorOnly).toLocaleString() }}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span v-else-if="row.vehicleRef.restrictions?.sponsorOnly !== undefined">
|
|
||||||
{{ new Date(row.vehicleRef.restrictions.sponsorOnly).toLocaleString() }}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span v-else>❌</span>
|
|
||||||
</td>
|
|
||||||
<td
|
|
||||||
class="editable"
|
|
||||||
:data-pending="
|
|
||||||
row.pendingChanges.restrictions?.teamOnly !== undefined &&
|
|
||||||
row.pendingChanges.restrictions.teamOnly !== row.vehicleRef.restrictions?.teamOnly
|
|
||||||
"
|
|
||||||
@click="editRowRestrictions(row, VehicleEditRestrictionKey.TEAM_ONLY)"
|
|
||||||
>
|
|
||||||
<span v-if="row.pendingChanges.restrictions?.teamOnly !== undefined">
|
|
||||||
{{ row.pendingChanges.restrictions.teamOnly ? '✔️' : '❌' }}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span v-else-if="row.vehicleRef.restrictions?.teamOnly !== undefined">
|
|
||||||
{{ row.vehicleRef.restrictions.teamOnly ? '✔️' : '❌' }}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span v-else>❌</span>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<keep-alive>
|
||||||
|
<component :is="tableTabs[currentTab]"></component>
|
||||||
|
</keep-alive>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts" setup>
|
||||||
import { defineComponent } from 'vue';
|
import { onMounted, ref } from 'vue';
|
||||||
import { useVehiclesStore } from '../stores/vehicles.store';
|
import { useVehiclesStore } from '../stores/vehicles.store';
|
||||||
import { VehicleEditRowKey, IVehicleTableRow, VehicleEditRestrictionKey } from '../types/vehicles.types';
|
|
||||||
import { IVehicle } from '../types/vehicles.types';
|
|
||||||
|
|
||||||
export default defineComponent({
|
import VehiclesTable from '../components/VehiclesManager/VehiclesTable.vue';
|
||||||
data: () => ({
|
import VehicleGroupsTable from '../components/VehiclesManager/VehicleGroupsTable.vue';
|
||||||
vehiclesStore: useVehiclesStore(),
|
|
||||||
vehicleSearchValue: '',
|
|
||||||
VehicleEditRowKey,
|
|
||||||
VehicleEditRestrictionKey,
|
|
||||||
}),
|
|
||||||
|
|
||||||
mounted() {
|
const tableTabs: Record<TableTabName, any> = {
|
||||||
this.vehiclesStore.fetchVehicles();
|
vehicles: VehiclesTable,
|
||||||
},
|
vehicleGroups: VehicleGroupsTable,
|
||||||
|
};
|
||||||
|
|
||||||
computed: {
|
type TableTabName = 'vehicles' | 'vehicleGroups';
|
||||||
vehiclesTableComp() {
|
|
||||||
return this.vehiclesStore.vehiclesTable
|
|
||||||
.filter((row) => row.vehicleRef.name.toLowerCase().startsWith(this.vehicleSearchValue.trim().toLowerCase()))
|
|
||||||
.sort((r1, r2) => {
|
|
||||||
//v1.name.localeCompare(v2.name, 'pl-PL')
|
|
||||||
|
|
||||||
return r1.vehicleRef.id - r2.vehicleRef.id;
|
const vehiclesStore = useVehiclesStore();
|
||||||
});
|
const currentTab = ref<TableTabName>('vehicles');
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
onMounted(() => {
|
||||||
editRowPrimitive(row: IVehicleTableRow, editKey: VehicleEditRowKey) {
|
vehiclesStore.fetchVehicles();
|
||||||
if (!(editKey in row.vehicleRef)) return;
|
|
||||||
|
|
||||||
const promptValue = prompt('Zmień wartość:', row.vehicleRef[editKey]);
|
|
||||||
if (promptValue == null) return;
|
|
||||||
|
|
||||||
let changedVehileObj: Partial<IVehicle> = {};
|
|
||||||
|
|
||||||
changedVehileObj[editKey] = promptValue;
|
|
||||||
|
|
||||||
if (row.vehicleRef[editKey] === promptValue) {
|
|
||||||
delete row.pendingChanges[editKey];
|
|
||||||
} else row.pendingChanges = { ...row.pendingChanges, ...changedVehileObj };
|
|
||||||
},
|
|
||||||
|
|
||||||
editRowRestrictions(row: IVehicleTableRow, editKey: VehicleEditRestrictionKey) {
|
|
||||||
let changedVehileObj: Partial<IVehicle> = {};
|
|
||||||
|
|
||||||
if (editKey == VehicleEditRestrictionKey.TEAM_ONLY) {
|
|
||||||
let nextTeamOnly = true;
|
|
||||||
|
|
||||||
if (row.pendingChanges.restrictions?.teamOnly !== undefined) nextTeamOnly = !row.pendingChanges.restrictions.teamOnly;
|
|
||||||
else if (row.vehicleRef.restrictions?.teamOnly !== undefined) nextTeamOnly = !row.vehicleRef.restrictions.teamOnly;
|
|
||||||
|
|
||||||
changedVehileObj['restrictions'] = {
|
|
||||||
teamOnly: nextTeamOnly,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (row.vehicleRef.restrictions?.teamOnly === changedVehileObj.restrictions.teamOnly) delete changedVehileObj['restrictions']['teamOnly'];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (editKey == VehicleEditRestrictionKey.SPONSOR_ONLY) {
|
|
||||||
const promptValue = prompt('Zmień wartość (timestamp):', row.vehicleRef.restrictions?.sponsorOnly?.toString() ?? Date.now().toString());
|
|
||||||
|
|
||||||
if (promptValue == null) return;
|
|
||||||
|
|
||||||
if (promptValue.trim() == '') {
|
|
||||||
changedVehileObj['restrictions'] = { sponsorOnly: undefined };
|
|
||||||
} else if (!isNaN(parseInt(promptValue))) {
|
|
||||||
const timestampPromptValue = parseInt(promptValue);
|
|
||||||
|
|
||||||
changedVehileObj['restrictions'] = { sponsorOnly: timestampPromptValue };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
row.pendingChanges = { ...row.pendingChanges, ...changedVehileObj };
|
|
||||||
|
|
||||||
// console.log(this.vehiclesStore.vehiclesTable.filter((v) => Object.keys(v.pendingChanges).length > 0));
|
|
||||||
},
|
|
||||||
|
|
||||||
getTableValueHTML(pendingValue: any, originalValue: any) {
|
|
||||||
if (pendingValue === undefined) return `${originalValue ?? '-'}`;
|
|
||||||
|
|
||||||
return `<s>${originalValue ?? '-'}</s> ${pendingValue}`;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function changeTab(tabName: TableTabName) {
|
||||||
|
currentTab.value = tabName;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss">
|
||||||
.view-wrapper {
|
.view-wrapper {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-rows: auto 1fr;
|
grid-template-rows: auto 1fr;
|
||||||
@@ -204,49 +63,51 @@ img.brand-image {
|
|||||||
max-width: 4em;
|
max-width: 4em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-box {
|
.mode-change-box {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5em;
|
||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For children tab elements
|
||||||
|
.table-search-box {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5em;
|
||||||
|
margin-top: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
.table-wrapper {
|
.table-wrapper {
|
||||||
|
width: 100%;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
table {
|
.table-wrapper table > thead > tr > td {
|
||||||
border-collapse: collapse;
|
& > div {
|
||||||
text-align: center;
|
display: flex;
|
||||||
table-layout: fixed;
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
thead {
|
gap: 0.5em;
|
||||||
position: sticky;
|
|
||||||
top: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
th {
|
&[data-sortable='true'] {
|
||||||
background-color: #2b2b2b;
|
|
||||||
}
|
|
||||||
|
|
||||||
tr:nth-child(odd) {
|
|
||||||
background-color: #3b3b3b;
|
|
||||||
}
|
|
||||||
|
|
||||||
tr:nth-child(even) {
|
|
||||||
background-color: #4c4c4c;
|
|
||||||
}
|
|
||||||
|
|
||||||
td.editable {
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
td[data-pending='true'] {
|
.table-wrapper table > tbody > tr {
|
||||||
background-color: lightgreen;
|
cursor: pointer;
|
||||||
color: black;
|
|
||||||
}
|
|
||||||
|
|
||||||
th,
|
&:hover {
|
||||||
td {
|
background: #1a293b;
|
||||||
padding: 0.5em;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.table-visible-results-box {
|
||||||
|
margin-top: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-visible-results-box input {
|
||||||
|
max-width: 70px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user