Fügt Sortierfunktionalität für die Mitgliederstatistik hinzu. Die Tabellenüberschriften sind jetzt klickbar und ermöglichen eine Sortierung nach Name und Teilnahmezahlen. Zudem wurde die Sortierreihenfolge implementiert und visuell durch Icons angezeigt.
This commit is contained in:
@@ -23,16 +23,36 @@
|
||||
<table class="members-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th @click="sortBy('name')" class="sortable-header">
|
||||
<div class="header-content">
|
||||
<span>Name</span>
|
||||
<span class="sort-icon">{{ getSortIcon('name') }}</span>
|
||||
</div>
|
||||
</th>
|
||||
<th>Geburtsdatum</th>
|
||||
<th>Teilnahmen (12 Monate)</th>
|
||||
<th>Teilnahmen (3 Monate)</th>
|
||||
<th>Teilnahmen (Gesamt)</th>
|
||||
<th @click="sortBy('participation12Months')" class="sortable-header">
|
||||
<div class="header-content">
|
||||
<span>Teilnahmen (12 Monate)</span>
|
||||
<span class="sort-icon">{{ getSortIcon('participation12Months') }}</span>
|
||||
</div>
|
||||
</th>
|
||||
<th @click="sortBy('participation3Months')" class="sortable-header">
|
||||
<div class="header-content">
|
||||
<span>Teilnahmen (3 Monate)</span>
|
||||
<span class="sort-icon">{{ getSortIcon('participation3Months') }}</span>
|
||||
</div>
|
||||
</th>
|
||||
<th @click="sortBy('participationTotal')" class="sortable-header">
|
||||
<div class="header-content">
|
||||
<span>Teilnahmen (Gesamt)</span>
|
||||
<span class="sort-icon">{{ getSortIcon('participationTotal') }}</span>
|
||||
</div>
|
||||
</th>
|
||||
<th>Aktionen</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="member in activeMembers" :key="member.id" class="member-row">
|
||||
<tr v-for="member in sortedMembers" :key="member.id" class="member-row">
|
||||
<td>{{ member.firstName }} {{ member.lastName }}</td>
|
||||
<td>{{ formatBirthdate(member.birthDate) }}</td>
|
||||
<td>{{ member.participation12Months }}</td>
|
||||
@@ -108,6 +128,32 @@ export default {
|
||||
if (this.activeMembers.length === 0) return 0;
|
||||
const total = this.activeMembers.reduce((sum, member) => sum + member.participation3Months, 0);
|
||||
return total / this.activeMembers.length;
|
||||
},
|
||||
|
||||
sortedMembers() {
|
||||
if (!this.activeMembers.length) return [];
|
||||
|
||||
return [...this.activeMembers].sort((a, b) => {
|
||||
let aValue, bValue;
|
||||
|
||||
if (this.sortField === 'name') {
|
||||
// Case-insensitive Namens-Sortierung mit localeCompare
|
||||
// Sortiere nach firstName lastName (wie in der Anzeige)
|
||||
const aName = `${a.firstName} ${a.lastName}`;
|
||||
const bName = `${b.firstName} ${b.lastName}`;
|
||||
return aName.localeCompare(bName, 'de', { sensitivity: 'base' }) * (this.sortDirection === 'asc' ? 1 : -1);
|
||||
} else {
|
||||
// Numerische Sortierung
|
||||
aValue = a[this.sortField] || 0;
|
||||
bValue = b[this.sortField] || 0;
|
||||
|
||||
if (this.sortDirection === 'asc') {
|
||||
return aValue > bValue ? 1 : -1;
|
||||
} else {
|
||||
return aValue < bValue ? 1 : -1;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
@@ -116,7 +162,9 @@ export default {
|
||||
activeMembers: [],
|
||||
showDetailsModal: false,
|
||||
selectedMember: {},
|
||||
loading: false
|
||||
loading: false,
|
||||
sortField: 'name',
|
||||
sortDirection: 'asc'
|
||||
};
|
||||
},
|
||||
|
||||
@@ -177,6 +225,24 @@ export default {
|
||||
formatDate(dateString) {
|
||||
const date = new Date(dateString);
|
||||
return `${String(date.getDate()).padStart(2, '0')}.${String(date.getMonth() + 1).padStart(2, '0')}.${date.getFullYear()}`;
|
||||
},
|
||||
|
||||
sortBy(field) {
|
||||
if (this.sortField === field) {
|
||||
// Wenn bereits nach diesem Feld sortiert wird, Richtung umkehren
|
||||
this.sortDirection = this.sortDirection === 'asc' ? 'desc' : 'asc';
|
||||
} else {
|
||||
// Neues Feld, Standardrichtung setzen
|
||||
this.sortField = field;
|
||||
this.sortDirection = 'desc';
|
||||
}
|
||||
},
|
||||
|
||||
getSortIcon(field) {
|
||||
if (this.sortField !== field) {
|
||||
return '↕️'; // Neutral
|
||||
}
|
||||
return this.sortDirection === 'asc' ? '↑' : '↓';
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -245,6 +311,29 @@ export default {
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.sortable-header {
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.sortable-header:hover {
|
||||
background: var(--primary-color) !important;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.header-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.sort-icon {
|
||||
font-size: 0.875rem;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.members-table td {
|
||||
padding: 1rem;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
|
||||
Reference in New Issue
Block a user