feat(MembersOverviewSection, MembersView): add age range filter and enhance member scope display

- Introduced age range inputs in the MembersOverviewSection for filtering members by age, allowing users to specify a minimum and maximum age.
- Updated the MembersView to handle new age range properties and emit corresponding updates.
- Enhanced the display of member scope chips by separating label and count for improved readability and styling.
- Refactored related components to ensure consistent functionality and styling across the application.
This commit is contained in:
Torsten Schulz (local)
2026-03-18 17:10:37 +01:00
parent 563a7e8dde
commit eea3372057
2 changed files with 102 additions and 6 deletions

View File

@@ -60,7 +60,8 @@
:class="{ 'scope-chip-active': selectedMemberScope === scope.value }"
@click="$emit('update:selected-member-scope', scope.value)"
>
{{ scope.label }} <span class="scope-chip-count">{{ scope.count }}</span>
<span class="scope-chip-label">{{ scope.label }}</span>
<span class="scope-chip-count">{{ scope.count }}</span>
</button>
</div>
</div>
@@ -86,8 +87,33 @@
<option value="J15">{{ $t('members.j15') }}</option>
<option value="J13">{{ $t('members.j13') }}</option>
<option value="J11">{{ $t('members.j11') }}</option>
<option value="range">Alter von - bis</option>
</select>
</div>
<div v-if="selectedAgeGroup === 'range'" class="filter-group age-range-group">
<label>Alter von - bis:</label>
<div class="age-range-inputs">
<input
:value="selectedAgeFrom"
type="number"
min="0"
max="120"
class="filter-select age-range-input"
placeholder="von"
@input="$emit('update:selected-age-from', $event.target.value)"
>
<span class="age-range-separator">-</span>
<input
:value="selectedAgeTo"
type="number"
min="0"
max="120"
class="filter-select age-range-input"
placeholder="bis"
@input="$emit('update:selected-age-to', $event.target.value)"
>
</div>
</div>
<div class="filter-group">
<label>{{ $t('members.gender') }}:</label>
<select :value="selectedGender" class="filter-select" @change="$emit('update:selected-gender', $event.target.value)">
@@ -198,6 +224,8 @@ export default {
selectedMemberScope: { type: String, required: true },
showInactiveMembers: { type: Boolean, required: true },
selectedAgeGroup: { type: String, required: true },
selectedAgeFrom: { type: [String, Number], required: true },
selectedAgeTo: { type: [String, Number], required: true },
selectedGender: { type: String, required: true },
selectedSort: { type: String, required: true },
sortDirection: { type: String, required: true },
@@ -216,6 +244,8 @@ export default {
'update:selected-member-scope',
'update:show-inactive-members',
'update:selected-age-group',
'update:selected-age-from',
'update:selected-age-to',
'update:selected-gender',
'clear-filters',
'update:selected-sort',
@@ -325,22 +355,64 @@ export default {
min-width: 11rem;
}
.age-range-group {
min-width: 14rem;
}
.age-range-inputs {
display: flex;
align-items: center;
gap: 0.4rem;
}
.age-range-input {
min-width: 5.5rem;
}
.age-range-separator {
color: var(--text-muted);
font-weight: 600;
}
.scope-chip {
display: inline-flex;
align-items: center;
gap: 0.5rem;
border: 1px solid var(--border-color);
border-radius: 999px;
padding: 0.4rem 0.7rem;
padding: 0.4rem 0.85rem;
background: white;
color: var(--text-color);
color: var(--text-primary);
box-shadow: none;
}
.scope-chip-active {
background: var(--primary-light);
color: var(--primary-dark);
color: var(--primary-strong);
border-color: var(--primary-color);
}
.scope-chip-label {
white-space: nowrap;
}
.scope-chip-count {
opacity: 0.75;
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 1.6rem;
padding: 0.1rem 0.45rem;
border-radius: 999px;
background: #eef2f5;
color: #4b5563;
font-size: 0.78rem;
font-weight: 700;
line-height: 1.2;
}
.scope-chip-active .scope-chip-count {
background: rgba(24, 70, 54, 0.12);
color: var(--primary-strong);
}
.members-export-card-wide {

View File

@@ -12,6 +12,8 @@
:selected-member-scope="selectedMemberScope"
:show-inactive-members="showInactiveMembers"
:selected-age-group="selectedAgeGroup"
:selected-age-from="selectedAgeFrom"
:selected-age-to="selectedAgeTo"
:selected-gender="selectedGender"
:selected-sort="selectedSort"
:sort-direction="sortDirection"
@@ -28,6 +30,8 @@
@update:selected-member-scope="selectedMemberScope = $event"
@update:show-inactive-members="showInactiveMembers = $event"
@update:selected-age-group="selectedAgeGroup = $event"
@update:selected-age-from="selectedAgeFrom = $event"
@update:selected-age-to="selectedAgeTo = $event"
@update:selected-gender="selectedGender = $event"
@clear-filters="clearFilters"
@update:selected-sort="selectedSort = $event"
@@ -566,12 +570,28 @@ export default {
}
// Altersklasse Filter
if (this.selectedAgeGroup) {
if (this.selectedAgeGroup && this.selectedAgeGroup !== 'range') {
const age = this.getAge(member.birthDate);
if (!this.matchesAgeGroup(age, this.selectedAgeGroup)) {
return false;
}
}
const age = this.getAge(member.birthDate);
const minAge = this.selectedAgeFrom !== '' ? Number.parseInt(this.selectedAgeFrom, 10) : null;
const maxAge = this.selectedAgeTo !== '' ? Number.parseInt(this.selectedAgeTo, 10) : null;
if (this.selectedAgeGroup === 'range' && (minAge !== null || maxAge !== null)) {
if (age === null) {
return false;
}
if (Number.isFinite(minAge) && age < minAge) {
return false;
}
if (Number.isFinite(maxAge) && age > maxAge) {
return false;
}
}
// Geschlecht Filter
if (this.selectedGender) {
@@ -776,6 +796,8 @@ export default {
selectedGroupToAdd: '',
showTransferDialog: false,
selectedAgeGroup: '',
selectedAgeFrom: '',
selectedAgeTo: '',
selectedGender: '',
clickTtPendingMemberIds: [],
searchQuery: '',
@@ -2173,6 +2195,8 @@ export default {
clearFilters() {
this.selectedAgeGroup = '';
this.selectedAgeFrom = '';
this.selectedAgeTo = '';
this.selectedGender = '';
this.showInactiveMembers = false;
this.searchQuery = '';