feat(memberController, memberRoutes, MembersView): implement Click-TT player registration feature

- Added a new endpoint for Click-TT player registration in memberController, allowing submission of existing player applications.
- Integrated the new endpoint into memberRoutes for handling requests.
- Updated MembersView to include a button for initiating Click-TT registration, with user confirmation and loading state management.
- Enhanced UI feedback for registration status, improving user experience during the application process.
This commit is contained in:
Torsten Schulz (local)
2026-03-11 13:17:59 +01:00
parent 9c30cd181c
commit 08095ce22e
5 changed files with 443 additions and 3 deletions

View File

@@ -255,6 +255,15 @@
<span v-if="member.active" @click.stop="quickDeactivateMember(member)" class="action-icon action-icon-deactivate" :title="$t('members.deactivateMember')">
</span>
<span
v-if="member.active"
@click.stop="requestClickTtRegistration(member)"
class="action-icon"
:class="{ 'action-icon-disabled': clickTtPendingMemberIds.includes(member.id) }"
:title="clickTtPendingMemberIds.includes(member.id) ? 'Click-TT-Antrag laeuft' : 'Spielberechtigung in click-TT beantragen'"
>
{{ clickTtPendingMemberIds.includes(member.id) ? '⏳' : '🏓' }}
</span>
<span @click.stop="openNotesModal(member)" class="action-icon" :title="$t('members.notes')">
📝
</span>
@@ -475,7 +484,8 @@ export default {
selectedGroupToAdd: '',
showTransferDialog: false,
selectedAgeGroup: '',
selectedGender: ''
selectedGender: '',
clickTtPendingMemberIds: []
}
},
async mounted() {
@@ -647,6 +657,37 @@ export default {
this.showInfo(this.$t('messages.error'), errorMessage, '', 'error');
}
},
async requestClickTtRegistration(member) {
if (this.clickTtPendingMemberIds.includes(member.id)) {
return;
}
const confirmed = await this.showConfirm(
'Click-TT-Antrag starten',
`Soll fuer ${member.firstName} ${member.lastName} der automatisierte Click-TT-Antrag gestartet werden?`,
'Aktuell ist nur der Workflow fuer bereits im click-TT-System vorhandene Spieler automatisiert.',
'info'
);
if (!confirmed) {
return;
}
this.clickTtPendingMemberIds = [...this.clickTtPendingMemberIds, member.id];
try {
const response = await apiClient.post(`/clubmembers/clicktt-registration/${this.currentClub}/${member.id}`);
if (response.data?.success) {
await this.showInfo('Click-TT-Antrag', getSafeMessage(response.data.message, 'Der Click-TT-Antrag wurde erfolgreich eingereicht.'), response.data.finalUrl || '', 'success');
} else {
await this.showInfo('Click-TT-Antrag', getSafeMessage(response.data?.error, 'Der Click-TT-Antrag konnte nicht eingereicht werden.'), '', 'error');
}
} catch (error) {
console.error('Click-TT-Antrag fehlgeschlagen', error);
const errorMessage = getSafeErrorMessage(error, 'Der Click-TT-Antrag konnte nicht eingereicht werden.');
await this.showInfo('Click-TT-Antrag', errorMessage, '', 'error');
} finally {
this.clickTtPendingMemberIds = this.clickTtPendingMemberIds.filter(id => id !== member.id);
}
},
toggleNewMember() {
this.memberFormIsOpen = !this.memberFormIsOpen;
},
@@ -1959,6 +2000,17 @@ table td {
opacity: 0.8;
}
.action-icon-disabled {
opacity: 0.5;
cursor: wait;
pointer-events: none;
}
.action-icon-disabled:hover {
transform: none;
opacity: 0.5;
}
.action-icon-deactivate {
filter: grayscale(0.3);
}