diff --git a/backend/controllers/memberController.js b/backend/controllers/memberController.js index c651565..ac9692f 100644 --- a/backend/controllers/memberController.js +++ b/backend/controllers/memberController.js @@ -124,6 +124,18 @@ const quickUpdateMemberFormHandedOver = async (req, res) => { } }; +const quickDeactivateMember = async (req, res) => { + try { + const { clubId, memberId } = req.params; + const { authcode: userToken } = req.headers; + const result = await MemberService.quickDeactivateMember(userToken, clubId, memberId); + res.status(result.status).json(result.response); + } catch (error) { + console.error('[quickDeactivateMember] - Error:', error); + res.status(500).json({ error: 'Failed to deactivate member' }); + } +}; + const transferMembers = async (req, res) => { try { const { id: clubId } = req.params; @@ -156,4 +168,4 @@ const transferMembers = async (req, res) => { } }; -export { getClubMembers, getWaitingApprovals, setClubMembers, uploadMemberImage, getMemberImage, updateRatingsFromMyTischtennis, rotateMemberImage, transferMembers, quickUpdateTestMembership, quickUpdateMemberFormHandedOver }; \ No newline at end of file +export { getClubMembers, getWaitingApprovals, setClubMembers, uploadMemberImage, getMemberImage, updateRatingsFromMyTischtennis, rotateMemberImage, transferMembers, quickUpdateTestMembership, quickUpdateMemberFormHandedOver, quickDeactivateMember }; \ No newline at end of file diff --git a/backend/routes/memberRoutes.js b/backend/routes/memberRoutes.js index 5549d6f..39ac51d 100644 --- a/backend/routes/memberRoutes.js +++ b/backend/routes/memberRoutes.js @@ -1,4 +1,4 @@ -import { getClubMembers, getWaitingApprovals, setClubMembers, uploadMemberImage, getMemberImage, updateRatingsFromMyTischtennis, rotateMemberImage, transferMembers, quickUpdateTestMembership, quickUpdateMemberFormHandedOver } from '../controllers/memberController.js'; +import { getClubMembers, getWaitingApprovals, setClubMembers, uploadMemberImage, getMemberImage, updateRatingsFromMyTischtennis, rotateMemberImage, transferMembers, quickUpdateTestMembership, quickUpdateMemberFormHandedOver, quickDeactivateMember } from '../controllers/memberController.js'; import express from 'express'; import { authenticate } from '../middleware/authMiddleware.js'; import { authorize } from '../middleware/authorizationMiddleware.js'; @@ -19,5 +19,6 @@ router.post('/rotate-image/:clubId/:memberId', authenticate, authorize('members' router.post('/transfer/:id', authenticate, authorize('members', 'write'), transferMembers); router.post('/quick-update-test-membership/:clubId/:memberId', authenticate, authorize('members', 'write'), quickUpdateTestMembership); router.post('/quick-update-member-form/:clubId/:memberId', authenticate, authorize('members', 'write'), quickUpdateMemberFormHandedOver); +router.post('/quick-deactivate/:clubId/:memberId', authenticate, authorize('members', 'write'), quickDeactivateMember); export default router; diff --git a/backend/services/memberService.js b/backend/services/memberService.js index 3bc2e62..1e534e7 100644 --- a/backend/services/memberService.js +++ b/backend/services/memberService.js @@ -544,6 +544,31 @@ class MemberService { return { status: 500, response: { error: 'Failed to update member form status' } }; } } + + async quickDeactivateMember(userToken, clubId, memberId) { + try { + await checkAccess(userToken, clubId); + const member = await Member.findOne({ where: { id: memberId, clubId: clubId } }); + if (!member) { + return { status: 404, response: { error: 'Member not found in this club' } }; + } + + if (!member.active) { + return { status: 400, response: { error: 'Member is already inactive' } }; + } + + member.active = false; + await member.save(); + + return { + status: 200, + response: { success: true, message: 'Mitglied deaktiviert' } + }; + } catch (error) { + console.error('[quickDeactivateMember] - Error:', error); + return { status: 500, response: { error: 'Failed to deactivate member' } }; + } + } } export default new MemberService(); \ No newline at end of file diff --git a/frontend/src/views/MembersView.vue b/frontend/src/views/MembersView.vue index 3a29d84..6d362b1 100644 --- a/frontend/src/views/MembersView.vue +++ b/frontend/src/views/MembersView.vue @@ -161,15 +161,22 @@ - -
- - - - +
+ + ✅ + + + 📄 + + + ⛔ + + + 📝 + + + 🏃 +
@@ -496,6 +503,26 @@ export default { this.showInfo('Fehler', errorMessage, '', 'error'); } }, + + async quickDeactivateMember(member) { + if (!confirm(`Möchten Sie "${member.firstName} ${member.lastName}" wirklich deaktivieren?`)) { + return; + } + + try { + const response = await apiClient.post(`/clubmembers/quick-deactivate/${this.currentClub}/${member.id}`); + if (response.data.success) { + member.active = false; + this.showInfo('Erfolg', response.data.message || 'Mitglied deaktiviert', '', 'success'); + } else { + this.showInfo('Fehler', response.data.error || 'Fehler beim Deaktivieren des Mitglieds', '', 'error'); + } + } catch (error) { + console.error('Fehler beim Deaktivieren des Mitglieds:', error); + const errorMessage = error.response?.data?.error || error.message || 'Fehler beim Deaktivieren des Mitglieds'; + this.showInfo('Fehler', errorMessage, '', 'error'); + } + }, toggleNewMember() { this.memberFormIsOpen = !this.memberFormIsOpen; }, @@ -1272,28 +1299,32 @@ table td { background-color: #138496; } -.action-buttons-row { +.action-icons-row { display: flex; - gap: 0.5rem; + gap: 0.75rem; flex-wrap: wrap; align-items: center; } -.btn-quick-action { - background-color: #ffc107; - color: #000; - border: none; - padding: 0.4rem 0.8rem; - border-radius: 4px; +.action-icon { + font-size: 1.2em; cursor: pointer; - font-size: 0.85em; - font-weight: 500; - transition: background-color 0.2s ease; - white-space: nowrap; + transition: transform 0.2s ease, opacity 0.2s ease; + display: inline-block; + line-height: 1; } -.btn-quick-action:hover { - background-color: #e0a800; +.action-icon:hover { + transform: scale(1.2); + opacity: 0.8; +} + +.action-icon-deactivate { + filter: grayscale(0.3); +} + +.action-icon-deactivate:hover { + filter: grayscale(0); } .warning-icon {