feat(MembersOverview): add last training filter and localization updates
All checks were successful
Deploy tt-tagebuch / deploy (push) Successful in 35s
All checks were successful
Deploy tt-tagebuch / deploy (push) Successful in 35s
- Introduced a new filter for last training in the MembersOverviewSection, allowing users to filter members based on their last training date. - Updated the MembersView to handle the new last training filter state and integrate it into the member sorting and display logic. - Enhanced localization files across multiple languages to include new terms related to the last training filter and its options. - Improved member row tooltip to display last training information, enhancing user experience and clarity.
This commit is contained in:
@@ -145,6 +145,20 @@
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-group">
|
||||
<label>{{ $t('members.lastTrainingFilter') }}</label>
|
||||
<select
|
||||
:value="selectedLastTrainingFilter"
|
||||
class="filter-select"
|
||||
@change="$emit('update:selected-last-training-filter', $event.target.value)"
|
||||
>
|
||||
<option value="">{{ $t('common.all') }}</option>
|
||||
<option value="hasDate">{{ $t('members.lastTrainingFilterHasDate') }}</option>
|
||||
<option value="noDate">{{ $t('members.lastTrainingFilterNoDate') }}</option>
|
||||
<option value="notInTraining">{{ $t('members.lastTrainingFilterNotInTraining') }}</option>
|
||||
</select>
|
||||
<p class="filter-hint filter-hint-tt">{{ $t('members.lastTrainingFilterHint') }}</p>
|
||||
</div>
|
||||
<button @click="$emit('clear-filters')" class="btn-clear-filters">{{ $t('members.clearFilters') }}</button>
|
||||
</div>
|
||||
</details>
|
||||
@@ -236,6 +250,7 @@ export default {
|
||||
selectedAgeTo: { type: [String, Number], required: true },
|
||||
selectedGender: { type: String, required: true },
|
||||
selectedTrainingGroup: { type: String, required: true },
|
||||
selectedLastTrainingFilter: { type: String, required: true },
|
||||
trainingGroupOptions: { type: Array, required: true },
|
||||
selectedSort: { type: String, required: true },
|
||||
sortDirection: { type: String, required: true },
|
||||
@@ -260,6 +275,7 @@ export default {
|
||||
'update:selected-age-to',
|
||||
'update:selected-gender',
|
||||
'update:selected-training-group',
|
||||
'update:selected-last-training-filter',
|
||||
'clear-filters',
|
||||
'update:selected-sort',
|
||||
'toggle-sort-direction',
|
||||
|
||||
@@ -472,6 +472,12 @@
|
||||
"ttSeasonCurrentTag": "aktuell",
|
||||
"ttSeasonNextTag": "kommend",
|
||||
"ttStichtagHint": "Stichtag 1.1. (DTTB). Jungen: nur J-Klassen. Mädchen: J und M möglich.",
|
||||
"lastTrainingFilter": "Letztes Training",
|
||||
"lastTrainingFilterHasDate": "Mit erfasstem Datum",
|
||||
"lastTrainingFilterNoDate": "Ohne letztes Training",
|
||||
"lastTrainingFilterNotInTraining": "„Nicht mehr im Training“",
|
||||
"lastTrainingFilterHint": "In der Tabelle: aktuelle Saison in der AK-Spalte. Alle Details (beide Saisons, letztes Training, ggf. Teilnahmen) beim Überfahren der Zeile.",
|
||||
"rowTooltipSeparator": "·",
|
||||
"ttAgeClassCol": "AK (TT)",
|
||||
"ttAdult": "Erwachsene (kein Jugend nach Stichtag)",
|
||||
"ttFilterGroupJ": "Mädchen & Jungen (gemischt)",
|
||||
|
||||
@@ -247,6 +247,12 @@
|
||||
"ttSeasonCurrentTag": "aktuell",
|
||||
"ttSeasonNextTag": "kommend",
|
||||
"ttStichtagHint": "Stichtag 1.1. (DTTB). Jungen: nur J-Klassen. Mädchen: J und M möglich.",
|
||||
"lastTrainingFilter": "Letztes Training",
|
||||
"lastTrainingFilterHasDate": "Mit erfasstem Datum",
|
||||
"lastTrainingFilterNoDate": "Ohne letztes Training",
|
||||
"lastTrainingFilterNotInTraining": "„Nicht mehr im Training“",
|
||||
"lastTrainingFilterHint": "In der Tabelle: aktuelle Saison in der AK-Spalte. Alle Details (beide Saisons, letztes Training, ggf. Teilnahmen) beim Überfahren der Zeile.",
|
||||
"rowTooltipSeparator": "·",
|
||||
"ttAgeClassCol": "AK (TT)",
|
||||
"ttAdult": "Erwachsene (kein Jugend nach Stichtag)",
|
||||
"ttFilterGroupJ": "Mädchen & Jungen (gemischt)",
|
||||
|
||||
@@ -472,6 +472,12 @@
|
||||
"ttSeasonCurrentTag": "current",
|
||||
"ttSeasonNextTag": "upcoming",
|
||||
"ttStichtagHint": "Cutoff 1 Jan (DTTB). Boys: J classes only. Girls: J and M.",
|
||||
"lastTrainingFilter": "Last training",
|
||||
"lastTrainingFilterHasDate": "With a recorded date",
|
||||
"lastTrainingFilterNoDate": "No last training",
|
||||
"lastTrainingFilterNotInTraining": "Flagged “no longer training”",
|
||||
"lastTrainingFilterHint": "Table: current season in the age-class column. Full detail (both seasons, last training, participations if known) on row hover.",
|
||||
"rowTooltipSeparator": "·",
|
||||
"ttAgeClassCol": "Age class (TT)",
|
||||
"ttAdult": "Adults (not youth by cutoff)",
|
||||
"ttFilterGroupJ": "Boys & girls (mixed)",
|
||||
|
||||
@@ -747,6 +747,12 @@
|
||||
"ttSeasonCurrentTag": "current",
|
||||
"ttSeasonNextTag": "upcoming",
|
||||
"ttStichtagHint": "Cutoff 1 Jan (DTTB). Boys: J classes only. Girls: J and M.",
|
||||
"lastTrainingFilter": "Last training",
|
||||
"lastTrainingFilterHasDate": "With a recorded date",
|
||||
"lastTrainingFilterNoDate": "No last training",
|
||||
"lastTrainingFilterNotInTraining": "Flagged “no longer training”",
|
||||
"lastTrainingFilterHint": "Table: current season in the age-class column. Full detail (both seasons, last training, participations if known) on row hover.",
|
||||
"rowTooltipSeparator": "·",
|
||||
"ttAgeClassCol": "Age class (TT)",
|
||||
"ttAdult": "Adults (not youth by cutoff)",
|
||||
"ttFilterGroupJ": "Boys & girls (mixed)",
|
||||
|
||||
@@ -472,6 +472,12 @@
|
||||
"ttSeasonCurrentTag": "current",
|
||||
"ttSeasonNextTag": "upcoming",
|
||||
"ttStichtagHint": "Cutoff 1 Jan (DTTB). Boys: J classes only. Girls: J and M.",
|
||||
"lastTrainingFilter": "Last training",
|
||||
"lastTrainingFilterHasDate": "With a recorded date",
|
||||
"lastTrainingFilterNoDate": "No last training",
|
||||
"lastTrainingFilterNotInTraining": "Flagged “no longer training”",
|
||||
"lastTrainingFilterHint": "Table: current season in the age-class column. Full detail (both seasons, last training, participations if known) on row hover.",
|
||||
"rowTooltipSeparator": "·",
|
||||
"ttAgeClassCol": "Age class (TT)",
|
||||
"ttAdult": "Adults (not youth by cutoff)",
|
||||
"ttFilterGroupJ": "Boys & girls (mixed)",
|
||||
|
||||
@@ -439,6 +439,12 @@
|
||||
"ttSeasonCurrentTag": "actual",
|
||||
"ttSeasonNextTag": "próxima",
|
||||
"ttStichtagHint": "Corte el 1 de enero (DTTB). Niños: solo clases J. Niñas: J y M.",
|
||||
"lastTrainingFilter": "Último entrenamiento",
|
||||
"lastTrainingFilterHasDate": "Con fecha registrada",
|
||||
"lastTrainingFilterNoDate": "Sin último entrenamiento",
|
||||
"lastTrainingFilterNotInTraining": "« Ya no entrena »",
|
||||
"lastTrainingFilterHint": "Tabla: temporada actual en la columna de edad. Detalle completo (ambas temporadas, último entrenamiento, participaciones) al pasar el ratón por la fila.",
|
||||
"rowTooltipSeparator": "·",
|
||||
"ttAgeClassCol": "Cat. edad (TT)",
|
||||
"ttAdult": "Adultos (no juvenil según corte)",
|
||||
"ttFilterGroupJ": "Niños y niñas (mixto)",
|
||||
|
||||
@@ -439,6 +439,12 @@
|
||||
"ttSeasonCurrentTag": "kasalukuyan",
|
||||
"ttSeasonNextTag": "susunod",
|
||||
"ttStichtagHint": "Cutoff Enero 1 (DTTB). Lalaki: J lang. Babae: J at M.",
|
||||
"lastTrainingFilter": "Huling training",
|
||||
"lastTrainingFilterHasDate": "May petsa",
|
||||
"lastTrainingFilterNoDate": "Walang huling training",
|
||||
"lastTrainingFilterNotInTraining": "Flag na “hindi na nagte-training”",
|
||||
"lastTrainingFilterHint": "Table: kasalukuyang season sa AK. Buong detalye (dalawang season, huling training, participations) kapag-hover sa hilera.",
|
||||
"rowTooltipSeparator": "·",
|
||||
"ttAgeClassCol": "Edad (TT)",
|
||||
"ttAdult": "Adulto (hindi youth sa cutoff)",
|
||||
"ttFilterGroupJ": "Babae at lalaki (halo)",
|
||||
|
||||
@@ -439,6 +439,12 @@
|
||||
"ttSeasonCurrentTag": "actuelle",
|
||||
"ttSeasonNextTag": "à venir",
|
||||
"ttStichtagHint": "Date limite le 1er janv. (DTTB). Garçons : classes J uniquement. Filles : J et M.",
|
||||
"lastTrainingFilter": "Dernier entraînement",
|
||||
"lastTrainingFilterHasDate": "Avec date enregistrée",
|
||||
"lastTrainingFilterNoDate": "Sans dernier entraînement",
|
||||
"lastTrainingFilterNotInTraining": "« Plus à l’entraînement »",
|
||||
"lastTrainingFilterHint": "Tableau : saison actuelle dans la colonne AK. Détail complet (deux saisons, dernier entraînement, participations) au survol de la ligne.",
|
||||
"rowTooltipSeparator": "·",
|
||||
"ttAgeClassCol": "Classe d'âge (TT)",
|
||||
"ttAdult": "Adultes (pas jeunes selon la date limite)",
|
||||
"ttFilterGroupJ": "Filles et garçons (mixte)",
|
||||
|
||||
@@ -439,6 +439,12 @@
|
||||
"ttSeasonCurrentTag": "attuale",
|
||||
"ttSeasonNextTag": "prossima",
|
||||
"ttStichtagHint": "Data di riferimento 1° gennaio (DTTB). Ragazzi: solo classi J. Ragazze: J e M.",
|
||||
"lastTrainingFilter": "Ultimo allenamento",
|
||||
"lastTrainingFilterHasDate": "Con data registrata",
|
||||
"lastTrainingFilterNoDate": "Senza ultimo allenamento",
|
||||
"lastTrainingFilterNotInTraining": "« Non più in allenamento »",
|
||||
"lastTrainingFilterHint": "Tabella: stagione corrente nella colonna AK. Dettaglio completo (entrambe le stagioni, ultimo allenamento, partecipazioni) al passaggio del mouse sulla riga.",
|
||||
"rowTooltipSeparator": "·",
|
||||
"ttAgeClassCol": "Classe (TT)",
|
||||
"ttAdult": "Adulti (non giovanili secondo la data)",
|
||||
"ttFilterGroupJ": "Ragazze e ragazzi (misto)",
|
||||
|
||||
@@ -439,6 +439,12 @@
|
||||
"ttSeasonCurrentTag": "今季",
|
||||
"ttSeasonNextTag": "来季",
|
||||
"ttStichtagHint": "基準日1月1日(DTTB)。男子:Jのみ。女子:JとM。",
|
||||
"lastTrainingFilter": "最終参加",
|
||||
"lastTrainingFilterHasDate": "日付あり",
|
||||
"lastTrainingFilterNoDate": "最終参加なし",
|
||||
"lastTrainingFilterNotInTraining": "「参加なし」フラグ",
|
||||
"lastTrainingFilterHint": "表:AK列は今シーズン。列にマウスを乗せると両シーズン・最終参加・参加回数(分かる場合)を表示。",
|
||||
"rowTooltipSeparator": "·",
|
||||
"ttAgeClassCol": "年齢(TT)",
|
||||
"ttAdult": "一般(基準日でジュニアではない)",
|
||||
"ttFilterGroupJ": "男女混合",
|
||||
|
||||
@@ -439,6 +439,12 @@
|
||||
"ttSeasonCurrentTag": "bieżący",
|
||||
"ttSeasonNextTag": "następny",
|
||||
"ttStichtagHint": "Termin 1.01 (DTTB). Chłopcy: tylko klasy J. Dziewczęta: J i M.",
|
||||
"lastTrainingFilter": "Ostatni trening",
|
||||
"lastTrainingFilterHasDate": "Z zapisaną datą",
|
||||
"lastTrainingFilterNoDate": "Bez ostatniego treningu",
|
||||
"lastTrainingFilterNotInTraining": "„Nie trenuje już”",
|
||||
"lastTrainingFilterHint": "Tabela: bieżący sezon w kolumnie AK. Pełne szczegóły (oba sezony, ostatni trening, udziały) po najechaniu na wiersz.",
|
||||
"rowTooltipSeparator": "·",
|
||||
"ttAgeClassCol": "Kategoria (TT)",
|
||||
"ttAdult": "Dorośli (brak juniora wg terminu)",
|
||||
"ttFilterGroupJ": "Dziewczęta i chłopcy (mieszane)",
|
||||
|
||||
@@ -439,6 +439,12 @@
|
||||
"ttSeasonCurrentTag": "ปัจจุบัน",
|
||||
"ttSeasonNextTag": "ถัดไป",
|
||||
"ttStichtagHint": "วันตัดสิทธิ์ 1 ม.ค. (DTTB) ชาย: เฉพาะ J หญิง: J และ M",
|
||||
"lastTrainingFilter": "การเข้าร่วมล่าสุด",
|
||||
"lastTrainingFilterHasDate": "มีวันที่บันทึก",
|
||||
"lastTrainingFilterNoDate": "ไม่มีการเข้าร่วมล่าสุด",
|
||||
"lastTrainingFilterNotInTraining": "ทำเครื่องหมาย «ไม่เข้าร่วมอีกต่อไป»",
|
||||
"lastTrainingFilterHint": "ตาราง: ฤดูกาลปัจจุบันในคอลัมน์ AK รายละเอียดครบ (สองฤดูกาล, การเข้าร่วมล่าสุด, จำนวนครั้ง) เมื่อชี้ที่แถว",
|
||||
"rowTooltipSeparator": "·",
|
||||
"ttAgeClassCol": "รุ่นอายุ (TT)",
|
||||
"ttAdult": "ผู้ใหญ่ (ไม่ใช่เยาวชนตามวันตัด)",
|
||||
"ttFilterGroupJ": "ชายและหญิง (รวม)",
|
||||
|
||||
@@ -439,6 +439,12 @@
|
||||
"ttSeasonCurrentTag": "kasalukuyan",
|
||||
"ttSeasonNextTag": "susunod",
|
||||
"ttStichtagHint": "Cutoff Enero 1 (DTTB). Lalaki: J lang. Babae: J at M.",
|
||||
"lastTrainingFilter": "Huling training",
|
||||
"lastTrainingFilterHasDate": "May petsa",
|
||||
"lastTrainingFilterNoDate": "Walang huling training",
|
||||
"lastTrainingFilterNotInTraining": "Flag na “hindi na nagte-training”",
|
||||
"lastTrainingFilterHint": "Table: kasalukuyang season sa AK. Buong detalye (dalawang season, huling training, participations) kapag-hover sa hilera.",
|
||||
"rowTooltipSeparator": "·",
|
||||
"ttAgeClassCol": "Edad (TT)",
|
||||
"ttAdult": "Adulto (hindi youth sa cutoff)",
|
||||
"ttFilterGroupJ": "Babae at lalaki (halo)",
|
||||
|
||||
@@ -439,6 +439,12 @@
|
||||
"ttSeasonCurrentTag": "当前",
|
||||
"ttSeasonNextTag": "下一",
|
||||
"ttStichtagHint": "截止日1月1日(DTTB)。男孩:仅 J 组。女孩:J 与 M。",
|
||||
"lastTrainingFilter": "最近训练",
|
||||
"lastTrainingFilterHasDate": "有记录日期",
|
||||
"lastTrainingFilterNoDate": "无最近训练",
|
||||
"lastTrainingFilterNotInTraining": "标记「不再参加训练」",
|
||||
"lastTrainingFilterHint": "表格:年龄组列显示当前赛季。悬停行可查看两赛季、最近训练及参与次数(若有)。",
|
||||
"rowTooltipSeparator": "·",
|
||||
"ttAgeClassCol": "年龄组(TT)",
|
||||
"ttAdult": "成人(按截止日非青年)",
|
||||
"ttFilterGroupJ": "男女孩(混合)",
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
:selected-age-to="selectedAgeTo"
|
||||
:selected-gender="selectedGender"
|
||||
:selected-training-group="selectedTrainingGroup"
|
||||
:selected-last-training-filter="selectedLastTrainingFilter"
|
||||
:training-group-options="trainingGroupFilterOptions"
|
||||
:selected-sort="selectedSort"
|
||||
:sort-direction="sortDirection"
|
||||
@@ -41,6 +42,7 @@
|
||||
@update:selected-age-to="selectedAgeTo = $event"
|
||||
@update:selected-gender="selectedGender = $event"
|
||||
@update:selected-training-group="selectedTrainingGroup = $event"
|
||||
@update:selected-last-training-filter="selectedLastTrainingFilter = $event"
|
||||
@clear-filters="clearFilters"
|
||||
@update:selected-sort="selectedSort = $event"
|
||||
@toggle-sort-direction="toggleSortDirection"
|
||||
@@ -345,7 +347,12 @@
|
||||
</thead>
|
||||
<tbody>
|
||||
<template v-for="member in sortedFilteredMembers" :key="member.id">
|
||||
<tr class="member-row" :class="{ 'row-selected': selectedMemberPreview && selectedMemberPreview.id === member.id, 'row-inactive': !member.active, 'row-test': member.testMembership && !member.memberFormHandedOver, 'row-test-form': member.testMembership && member.memberFormHandedOver }" @click="selectMember(member)">
|
||||
<tr
|
||||
class="member-row"
|
||||
:class="{ 'row-selected': selectedMemberPreview && selectedMemberPreview.id === member.id, 'row-inactive': !member.active, 'row-test': member.testMembership && !member.memberFormHandedOver, 'row-test-form': member.testMembership && member.memberFormHandedOver }"
|
||||
:title="memberRowHoverTooltip(member)"
|
||||
@click="selectMember(member)"
|
||||
>
|
||||
<td>
|
||||
<div @click.stop="openImageModal(member)">
|
||||
<img
|
||||
@@ -424,15 +431,11 @@
|
||||
<td>{{ getFormattedBirthdate(member.birthDate) }}</td>
|
||||
<td>{{ getAgeLabel(member.birthDate) }}</td>
|
||||
<td class="tt-age-class-cell">
|
||||
<div
|
||||
v-for="(line, idx) in formatMemberTtAgeClassLines(member)"
|
||||
:key="idx"
|
||||
class="tt-age-class-line"
|
||||
>
|
||||
{{ line }}
|
||||
</div>
|
||||
<span class="tt-age-class-compact">{{ formatMemberTtAgeClassCompactLine(member) }}</span>
|
||||
</td>
|
||||
<td class="last-training-cell">
|
||||
<span class="last-training-compact">{{ getOptionalFormattedDate(member.lastTraining, 'members.previewNoLastTraining') }}</span>
|
||||
</td>
|
||||
<td>{{ getOptionalFormattedDate(member.lastTraining, 'members.previewNoLastTraining') }}</td>
|
||||
<td v-if="showTrainingParticipationsColumn" class="training-participations-cell">
|
||||
{{ trainingParticipationsDisplay(member) }}
|
||||
</td>
|
||||
@@ -736,6 +739,16 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
if (this.selectedLastTrainingFilter === 'hasDate' && !this.hasValidLastTrainingDate(member)) {
|
||||
return false;
|
||||
}
|
||||
if (this.selectedLastTrainingFilter === 'noDate' && this.hasValidLastTrainingDate(member)) {
|
||||
return false;
|
||||
}
|
||||
if (this.selectedLastTrainingFilter === 'notInTraining' && !member.notInTraining) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (search) {
|
||||
const haystack = [
|
||||
member.firstName,
|
||||
@@ -977,6 +990,7 @@ export default {
|
||||
selectedAgeTo: '',
|
||||
selectedGender: '',
|
||||
selectedTrainingGroup: '',
|
||||
selectedLastTrainingFilter: '',
|
||||
clickTtPendingMemberIds: [],
|
||||
searchQuery: '',
|
||||
selectedMemberScope: 'active',
|
||||
@@ -2423,6 +2437,29 @@ export default {
|
||||
: age;
|
||||
},
|
||||
|
||||
hasValidLastTrainingDate(member) {
|
||||
if (!member?.lastTraining) return false;
|
||||
const d = new Date(member.lastTraining);
|
||||
return !Number.isNaN(d.getTime());
|
||||
},
|
||||
formatMemberTtAgeClassCompactLine(member) {
|
||||
const lines = this.formatMemberTtAgeClassLines(member);
|
||||
return lines[0] || '–';
|
||||
},
|
||||
memberRowHoverTooltip(member) {
|
||||
const akFull = this.formatMemberTtAgeClassLines(member).join(` ${this.$t('members.rowTooltipSeparator')} `);
|
||||
const dateLabel = this.$t('members.previewLastTraining');
|
||||
const dateVal = this.getOptionalFormattedDate(member.lastTraining, 'members.previewNoLastTraining');
|
||||
const parts = [`${this.$t('members.ttAgeClassCol')}: ${akFull}`, `${dateLabel}: ${dateVal}`];
|
||||
if (member.notInTraining) {
|
||||
parts.push(this.$t('members.notInTrainingTooltip', { weeks: member.missedTrainingWeeks || 0 }));
|
||||
}
|
||||
const tp = member.trainingParticipations;
|
||||
if (tp != null && !Number.isNaN(Number(tp))) {
|
||||
parts.push(`${this.$t('members.trainingParticipations')}: ${tp}`);
|
||||
}
|
||||
return parts.join(` ${this.$t('members.rowTooltipSeparator')} `);
|
||||
},
|
||||
formatMemberTtAgeClassLines(member) {
|
||||
const y0 = getSeasonStartYearFromDate();
|
||||
return [y0, y0 + 1].map((seasonYear) => {
|
||||
@@ -2460,6 +2497,7 @@ export default {
|
||||
this.selectedAgeTo = '';
|
||||
this.selectedGender = '';
|
||||
this.selectedTrainingGroup = '';
|
||||
this.selectedLastTrainingFilter = '';
|
||||
this.showInactiveMembers = false;
|
||||
this.searchQuery = '';
|
||||
this.selectedMemberScope = 'active';
|
||||
@@ -3383,11 +3421,26 @@ table td {
|
||||
font-size: 0.88rem;
|
||||
line-height: 1.35;
|
||||
vertical-align: top;
|
||||
max-width: 11rem;
|
||||
}
|
||||
|
||||
.tt-age-class-line + .tt-age-class-line {
|
||||
margin-top: 0.2rem;
|
||||
opacity: 0.92;
|
||||
.tt-age-class-compact {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.last-training-cell {
|
||||
max-width: 9rem;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.last-training-compact {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.training-participations-cell {
|
||||
|
||||
Reference in New Issue
Block a user