Fügt Unterstützung für Aktivitätenmitglieder in DiaryView.vue hinzu. Ermöglicht das Zuordnen von Teilnehmern zu Aktivitäten, einschließlich der Verwaltung von Teilnehmern über das Backend. Aktualisiert die Datenbankmodelle und -routen, um die neuen Funktionen zu unterstützen.
This commit is contained in:
@@ -136,7 +136,19 @@
|
||||
v-if="item.durationText && item.durationText.trim() !== ''"> ({{
|
||||
item.durationText }})</span>
|
||||
</td>
|
||||
<td><button @click="removePlanItem(item.id)" class="trash-btn">🗑️</button></td>
|
||||
<td>
|
||||
<button @click="toggleActivityMembers(item)" title="Teilnehmer zuordnen" class="person-btn">👤</button>
|
||||
<button @click="removePlanItem(item.id)" class="trash-btn">🗑️</button>
|
||||
<div v-if="activityMembersOpenId === item.id" class="dropdown" style="max-height: 12em; padding: 0.25rem;">
|
||||
<div style="margin-bottom: 0.25rem; font-weight: 600;">Teilnehmer zuordnen</div>
|
||||
<div style="max-height: 9.5em; overflow-y: auto;">
|
||||
<label v-for="m in presentMembers" :key="m.id" style="display:flex; align-items:center; gap:0.5rem;">
|
||||
<input type="checkbox" :checked="isAssignedToActivity(item.id, m.id)" @change="toggleMemberForActivity(item.id, m.id, $event.target.checked)">
|
||||
<span>{{ m.firstName }} {{ m.lastName }}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<template v-for="groupItem in item.groupActivities">
|
||||
<tr>
|
||||
@@ -220,10 +232,10 @@
|
||||
{{ activity.description }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<multiselect v-model="selectedActivityTags" :options="availableTags" placeholder="Tags auswählen"
|
||||
<multiselect v-model="selectedActivityTags" :options="availableTags" placeholder="Tags auswählen"
|
||||
label="name" track-by="id" multiple :close-on-select="true" @tag="addNewTag"
|
||||
@remove="removeActivityTag" :allow-empty="false" @keydown.enter.prevent="addNewTagFromInput" />
|
||||
</div>
|
||||
<h3>Teilnehmer ({{ participants.length }})</h3>
|
||||
<ul>
|
||||
<li v-for="member in sortedMembers()" :key="member.id" class="checkbox-item">
|
||||
@@ -406,6 +418,9 @@ export default {
|
||||
newItemSearchResults: [],
|
||||
// Aktivitäten-Box (rechts)
|
||||
showActivitiesBox: false,
|
||||
// Aktivitäts-Teilnehmer
|
||||
activityMembersOpenId: null,
|
||||
activityMembersMap: {}, // key: activityId, value: Set(participantIds)
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
@@ -433,6 +448,11 @@ export default {
|
||||
activity.name.toLowerCase().includes(input)
|
||||
);
|
||||
},
|
||||
presentMembers() {
|
||||
// participants enthält memberIds der anwesenden
|
||||
const presentSet = new Set(this.participants);
|
||||
return this.members.filter(m => presentSet.has(m.id));
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async init() {
|
||||
@@ -529,6 +549,8 @@ export default {
|
||||
async loadParticipants(dateId) {
|
||||
const response = await apiClient.get(`/participants/${dateId}`);
|
||||
this.participants = response.data.map(participant => participant.memberId);
|
||||
// Map für memberId -> participantId speichern
|
||||
this.participantMapByMemberId = response.data.reduce((map, p) => { map[p.memberId] = p.id; return map; }, {});
|
||||
},
|
||||
|
||||
async loadActivities(dateId) {
|
||||
@@ -1262,6 +1284,72 @@ export default {
|
||||
toggleActivitiesBox() {
|
||||
this.showActivitiesBox = !this.showActivitiesBox;
|
||||
},
|
||||
|
||||
async toggleActivityMembers(item) {
|
||||
if (this.activityMembersOpenId === item.id) {
|
||||
this.activityMembersOpenId = null;
|
||||
return;
|
||||
}
|
||||
this.activityMembersOpenId = item.id;
|
||||
await this.ensureActivityMembersLoaded(item.id);
|
||||
},
|
||||
|
||||
async ensureActivityMembersLoaded(activityId) {
|
||||
if (this.activityMembersMap[activityId]) return;
|
||||
try {
|
||||
const r = await apiClient.get(`/diary-member-activities/${this.currentClub}/${activityId}`);
|
||||
const setIds = new Set(r.data.map(x => x.participantId));
|
||||
this.$set ? this.$set(this.activityMembersMap, activityId, setIds) : (this.activityMembersMap[activityId] = setIds);
|
||||
} catch (e) {
|
||||
this.activityMembersMap[activityId] = new Set();
|
||||
}
|
||||
},
|
||||
|
||||
isAssignedToActivity(activityId, memberId) {
|
||||
const participantId = this.participantIdForMember(memberId);
|
||||
const setIds = this.activityMembersMap[activityId];
|
||||
return setIds ? setIds.has(participantId) : false;
|
||||
},
|
||||
|
||||
participantIdForMember(memberId) {
|
||||
// In diesem UI haben wir nur memberIds; Participant-IDs müssen über current date abgeleitet werden.
|
||||
// Vereinfachung: Participant-ID ist Kombination aus (date.id, memberId) → wir müssen sie vom Backend bekommen.
|
||||
// Als Pragmatik: wir holen sie über /participants und mappen nach memberId. Bereits vorhanden in participants-Liste? Nein, nur IDs.
|
||||
// Für add/remove nutzen wir einen Helper-Endpoint? Wir haben bereits /participants für dateId → wir bauen Map:
|
||||
// Wir speichern eine lokale Map beim Laden der Teilnehmer.
|
||||
const map = this.participantMapByMemberId || {};
|
||||
return map[memberId];
|
||||
},
|
||||
|
||||
async toggleMemberForActivity(activityId, memberId, checked) {
|
||||
let participantId = this.participantIdForMember(memberId);
|
||||
try {
|
||||
if (checked) {
|
||||
// Falls Mitglied noch kein Participant ist: zuerst hinzufügen
|
||||
if (!participantId) {
|
||||
const created = await apiClient.post('/participants/add', {
|
||||
diaryDateId: this.date.id,
|
||||
memberId: memberId,
|
||||
});
|
||||
participantId = created.data.id;
|
||||
if (!this.participantMapByMemberId) this.participantMapByMemberId = {};
|
||||
this.participantMapByMemberId[memberId] = participantId;
|
||||
if (!this.participants.includes(memberId)) this.participants.push(memberId);
|
||||
}
|
||||
// Danach Zuordnung zur Aktivität anlegen
|
||||
await apiClient.post(`/diary-member-activities/${this.currentClub}/${activityId}`, { participantIds: [participantId] });
|
||||
if (!this.activityMembersMap[activityId]) this.activityMembersMap[activityId] = new Set();
|
||||
this.activityMembersMap[activityId].add(participantId);
|
||||
} else {
|
||||
// Nur die Aktivitäts-Zuordnung entfernen (Participant bleibt)
|
||||
if (!participantId) return;
|
||||
await apiClient.delete(`/diary-member-activities/${this.currentClub}/${activityId}/${participantId}`);
|
||||
if (this.activityMembersMap[activityId]) this.activityMembersMap[activityId].delete(participantId);
|
||||
}
|
||||
} catch (e) {
|
||||
alert('Fehler beim Aktualisieren der Aktivitäts-Teilnehmer');
|
||||
}
|
||||
},
|
||||
},
|
||||
async mounted() {
|
||||
await this.init();
|
||||
@@ -1630,4 +1718,8 @@ img {
|
||||
.collapsible-box.expanded h3 span {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.person-btn {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user