mirror of
https://github.com/Spythere/station-manager-2.0.git
synced 2026-05-03 13:38:13 +00:00
chore: added sorting icons to vehicles table
This commit is contained in:
@@ -1,22 +1,40 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="table-search-box">
|
<div class="table-search-box">
|
||||||
<input type="text" placeholder="Wyszukaj pojazd..." v-model="vehicleSearchInput" />
|
<input type="text" placeholder="Wyszukaj pojazd..." v-model="vehicleSearchInput" />
|
||||||
<button @click="addVehicleRow()">DODAJ NOWY POJAZD</button>
|
|
||||||
|
<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>
|
</div>
|
||||||
|
|
||||||
<div class="table-wrapper">
|
<div class="table-wrapper">
|
||||||
<table class="vehicle-manager-table">
|
<table class="vehicle-manager-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<td style="width: 50px">#</td>
|
<td
|
||||||
<td style="width: 200px">Nazwa</td>
|
v-for="header in headers"
|
||||||
<td style="width: 150px">Typ</td>
|
:style="`width: ${header.elementWidth}px`"
|
||||||
<td style="width: 150px">Kabina (lok.)</td>
|
@click="sortTableBy(header.id)"
|
||||||
<td style="width: 170px">Grupa</td>
|
:data-sortable="header.sortable"
|
||||||
<td style="width: 100px">Tylko sponsorzy do</td>
|
>
|
||||||
<td style="width: 100px">Tylko zespół</td>
|
<div>
|
||||||
<td style="width: 50px">Ukryty</td>
|
{{ header.title }}
|
||||||
<td style="width: 50px">Usuń</td>
|
|
||||||
|
<LucideArrowUp :size="18" v-if="activeSortKey === header.id && activeSortDir == 1" />
|
||||||
|
<LucideArrowDown :size="18" v-else-if="activeSortKey === header.id" />
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|
||||||
@@ -76,7 +94,9 @@
|
|||||||
{{ row.vehicleRef.hidden ? '✅' : '❌' }}
|
{{ row.vehicleRef.hidden ? '✅' : '❌' }}
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td class="editable" @click="removeVehicle(row.vehicleRef.id)"><img src="/icon-trash.svg" alt="remove" /></td>
|
<td class="editable" @click="removeVehicleRow(row.vehicleRef.id)">
|
||||||
|
<img src="/icon-trash.svg" alt="remove" />
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
@@ -86,21 +106,71 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, nextTick, ref, useTemplateRef } from 'vue';
|
import { computed, nextTick, ref, useTemplateRef } from 'vue';
|
||||||
import { useVehiclesStore } from '../../stores/vehicles.store';
|
import { useVehiclesStore } from '../../stores/vehicles.store';
|
||||||
import { IVehicleTableRow, VehicleEditRestrictionKey, VehicleEditRowKey } from '../../types/vehicles.types';
|
import { IVehicle, IVehicleTableRow, VehicleEditRestrictionKey, VehicleEditRowKey } from '../../types/vehicles.types';
|
||||||
|
import { LucideArrowDown, LucideArrowUp, LucidePlus, LucideX } from 'lucide-vue-next';
|
||||||
|
|
||||||
|
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 },
|
||||||
|
{ id: 'remove', elementWidth: 75, title: 'Usuń', sortable: false },
|
||||||
|
];
|
||||||
|
|
||||||
const vehiclesStore = useVehiclesStore();
|
const vehiclesStore = useVehiclesStore();
|
||||||
|
|
||||||
const vehicleSearchInput = ref('');
|
const vehicleSearchInput = ref('');
|
||||||
|
const maxVisibleResults = ref(50);
|
||||||
|
const activeSortKey = ref<string>('id');
|
||||||
|
const activeSortDir = ref(1);
|
||||||
|
|
||||||
const currentEditingGroupId = ref(-1);
|
const currentEditingGroupId = ref(-1);
|
||||||
const selectGroup = useTemplateRef('select-group');
|
const selectGroup = useTemplateRef('select-group');
|
||||||
|
|
||||||
const vehiclesTableComp = computed(() => {
|
const vehiclesTableComp = computed(() => {
|
||||||
return vehiclesStore.vehiclesTable
|
return vehiclesStore.vehiclesTable
|
||||||
.filter((row) => row.vehicleRef.name.toLowerCase().includes(vehicleSearchInput.value.trim().toLowerCase()))
|
.filter((row) => row.vehicleRef.name.toLowerCase().includes(vehicleSearchInput.value.trim().toLowerCase()))
|
||||||
.sort((r1, r2) => {
|
.sort((v1, v2) => sorterFunctions[activeSortKey.value](v1.vehicleRef, v2.vehicleRef))
|
||||||
return r1.vehicleRef.id - r2.vehicleRef.id;
|
.filter((_, i) => i < maxVisibleResults.value);
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
async function editRowPrimitive(row: IVehicleTableRow, editKey: VehicleEditRowKey) {
|
async function editRowPrimitive(row: IVehicleTableRow, editKey: VehicleEditRowKey) {
|
||||||
if (!(editKey in row.vehicleRef)) return;
|
if (!(editKey in row.vehicleRef)) return;
|
||||||
|
|
||||||
@@ -175,7 +245,6 @@ async function selectRowVehicleGroup(row: IVehicleTableRow) {
|
|||||||
async function editVehicleGroup(e: Event, row: IVehicleTableRow) {
|
async function editVehicleGroup(e: Event, row: IVehicleTableRow) {
|
||||||
const id = (e.target as HTMLSelectElement).value;
|
const id = (e.target as HTMLSelectElement).value;
|
||||||
|
|
||||||
|
|
||||||
if (row.vehicleRef.group.id !== +id) {
|
if (row.vehicleRef.group.id !== +id) {
|
||||||
const updatedData = await vehiclesStore.updateVehicle(row.vehicleRef.id, 'vehicleGroupId', +id);
|
const updatedData = await vehiclesStore.updateVehicle(row.vehicleRef.id, 'vehicleGroupId', +id);
|
||||||
|
|
||||||
@@ -210,7 +279,7 @@ async function addVehicleRow() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function removeVehicle(id: number) {
|
async function removeVehicleRow(id: number) {
|
||||||
const confirmRemove = confirm('Czy na pewno chcesz usunąć ten pojazd?');
|
const confirmRemove = confirm('Czy na pewno chcesz usunąć ten pojazd?');
|
||||||
|
|
||||||
if (!confirmRemove) return;
|
if (!confirmRemove) return;
|
||||||
@@ -222,5 +291,3 @@ async function removeVehicle(id: number) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style></style>
|
|
||||||
|
|||||||
+12
-2
@@ -48,13 +48,17 @@ table thead {
|
|||||||
top: 0;
|
top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
table thead td {
|
table thead tr td {
|
||||||
padding: 0.4rem 0.45rem;
|
padding: 0.4rem 0.45rem;
|
||||||
background-color: #151b24;
|
background-color: #151b24;
|
||||||
color: white;
|
color: white;
|
||||||
top: 0;
|
top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table thead tr td img {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
table tbody tr {
|
table tbody tr {
|
||||||
background-color: #2c394b;
|
background-color: #2c394b;
|
||||||
transition: background-color 100ms;
|
transition: background-color 100ms;
|
||||||
@@ -77,7 +81,7 @@ table tbody tr td {
|
|||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
td img {
|
table tbody tr td img {
|
||||||
height: 1.45em;
|
height: 1.45em;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
@@ -125,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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,4 +81,25 @@ img.brand-image {
|
|||||||
overflow: auto;
|
overflow: auto;
|
||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.table-wrapper table > thead > tr > td {
|
||||||
|
& > div {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-sortable='true'] {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.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