chore: added vehicle groups table data & vehicle groups api CRUD support

This commit is contained in:
2025-11-26 19:14:02 +01:00
parent 8b42d2c395
commit 3698552e28
4 changed files with 315 additions and 66 deletions
@@ -1,12 +1,26 @@
<template>
<div class="table-search-box">
<input type="text" placeholder="Wyszukaj grupę..." v-model="vehicleGroupSearchValue" />
<button @click="addVehicleGroupRow()">DODAJ NOWY POJAZD</button>
<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" />
&nbsp;NOWA GRUPA
</button>
</div>
<div class="table-visible-results-box">
Pokazuj maks.
<input type="number" min="1" v-model="maxVisibleResults" />
wyników
</div>
<div class="table-wrapper">
<table class="vehicle-manager-table">
<thead>
<!-- <thead>
<tr>
<td style="width: 50px">#</td>
<td style="width: 200px">Nazwa</td>
@@ -20,41 +34,73 @@
<td style="width: 80px">Podwójna obsada</td>
<td style="width: 200px">Prędkości wg masy</td>
</tr>
</thead> -->
<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">
<td>{{ row.vehicleGroupRef.id }}</td>
<td class="editable">
{{ row.vehicleGroupRef.name }}
</td>
<td class="editable">
{{ row.vehicleGroupRef.length }}
</td>
<td class="editable">
{{ row.vehicleGroupRef.weight }}
</td>
<td class="editable">
{{ row.vehicleGroupRef.speed }}
</td>
<td class="editable">
{{ row.vehicleGroupRef.speedLoco }}
</td>
<td class="editable">
{{ row.vehicleGroupRef.speedLoaded }}
</td>
<td class="editable">
{{ row.vehicleGroupRef._count.vehicles }}
</td>
<td class="editable">
{{ row.vehicleGroupRef.locoProps?.coldStart ? '✅' : '❌' }}
</td>
<td class="editable">
{{ row.vehicleGroupRef.locoProps?.doubleManned ? '✅' : '❌' }}
</td>
<td class="editable">
{{ row.vehicleGroupRef.massSpeeds ? 'WPISANE' : 'BRAK' }}
</td>
<td class="editable" @click="removeVehicleGroupRow(row.vehicleGroupRef.id)">
<img src="/icon-trash.svg" alt="remove" />
</td>
</tr>
</tbody>
</table>
@@ -62,23 +108,137 @@
</template>
<script lang="ts" setup>
import { computed, ref } from 'vue';
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';
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 },
{ id: 'remove', elementWidth: 75, title: 'Usuń', sortable: false },
];
const vehiclesStore = useVehiclesStore();
const vehicleGroupSearchValue = ref('');
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(vehicleGroupSearchValue.value.trim().toLowerCase()),
row.vehicleGroupRef.name.toLowerCase().startsWith(vehicleGroupSearchInput.value.trim().toLowerCase()),
)
.sort((r1, r2) => {
return r1.vehicleGroupRef.id - r2.vehicleGroupRef.id;
});
.sort((r1, r2) => sorterFunctions[activeSortKey.value](r1.vehicleGroupRef, r2.vehicleGroupRef))
.filter((_, i) => i < maxVisibleResults.value);
});
function addVehicleGroupRow() {}
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;
}
// async function editRowPrimitive(row: IVehicleGroupTableRow, editKey: VehicleGroupEditRowKey) {
// if (!(editKey in row.vehicleGroupRef)) return;
// let rowValue = row.vehicleGroupRef[editKey];
// if (typeof rowValue === 'string' || typeof rowValue === 'undefined' || rowValue == null) {
// const promptValue = prompt('Zmień wartość:', rowValue ?? '');
// if (promptValue == null) return;
// const updatedData = await vehiclesStore.updateVehicle(row.vehicleGroupRef.id, editKey, promptValue);
// if (updatedData) {
// (row.vehicleGroupRef[editKey] as any) = updatedData[editKey];
// }
// } else if (typeof rowValue == 'boolean') {
// const updatedData = await vehiclesStore.updateVehicle(row.vehicleGroupRef.id, editKey, !rowValue);
// if (updatedData) {
// (row.vehicleGroupRef[editKey] as any) = updatedData[editKey];
// }
// }
// }
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;
}
}
async function removeVehicleGroupRow(id: number) {
const confirmRemove = confirm('Czy na pewno chcesz usunąć ten pojazd?');
if (!confirmRemove) return;
const removedData = await vehiclesStore.removeVehicle(id);
if (removedData) {
vehiclesStore.vehicleGroupsTable = vehiclesStore.vehicleGroupsTable.filter((v) => v.vehicleGroupRef.id != id);
}
}
</script>
<style lang="scss" scoped></style>
@@ -104,7 +104,7 @@
</template>
<script lang="ts" setup>
import { computed, nextTick, ref, useTemplateRef } from 'vue';
import { computed, nextTick, onMounted, ref, useTemplateRef, watch } from 'vue';
import { useVehiclesStore } from '../../stores/vehicles.store';
import { IVehicle, IVehicleTableRow, VehicleEditRestrictionKey, VehicleEditRowKey } from '../../types/vehicles.types';
import { LucideArrowDown, LucideArrowUp, LucidePlus, LucideX } from 'lucide-vue-next';
@@ -144,7 +144,7 @@ const headers: TableHeader[] = [
const vehiclesStore = useVehiclesStore();
const vehicleSearchInput = ref('');
const maxVisibleResults = ref(50);
const maxVisibleResults = ref(150);
const activeSortKey = ref<string>('id');
const activeSortDir = ref(1);
@@ -158,6 +158,14 @@ const vehiclesTableComp = computed(() => {
.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 = '';
}