Enhance participant update handling and UI responsiveness in DiaryView

This commit improves the participant update process by ensuring the latest participant data is fetched from the database before emitting socket events. It also refines the DiaryView component's UI, adding better handling for dropdowns and member group selections, enhancing user experience. Additionally, new CSS styles are introduced for member group select elements to ensure consistent appearance across browsers.
This commit is contained in:
Torsten Schulz (local)
2025-11-13 18:18:31 +01:00
parent 58e773e51e
commit 2b06a8dd10
4 changed files with 166 additions and 52 deletions

View File

@@ -77,7 +77,12 @@ export const onParticipantRemoved = (callback) => {
export const onParticipantUpdated = (callback) => {
if (socket) {
socket.on('participant:updated', callback);
socket.on('participant:updated', (data) => {
console.log('📡 [Socket] participant:updated empfangen:', data);
callback(data);
});
} else {
console.warn('⚠️ [Socket] onParticipantUpdated: Socket nicht verbunden');
}
};

View File

@@ -189,37 +189,39 @@
</span>
</td>
<td>
<button v-if="!item.isTimeblock" @click="toggleActivityMembers(item)" title="Teilnehmer zuordnen"
class="person-btn">👤</button>
<button v-if="item.isTimeblock" @click="addGroupActivityToTimeblock(item.id)" title="Gruppen-Aktivität hinzufügen"
class="btn-primary" style="font-size: 12px; padding: 2px 6px;">+ Gruppe</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; display: flex; align-items: center; gap: 0.5rem;">
<span>Teilnehmer zuordnen</span>
<button @click="assignAllMembersToActivity(item.id)"
style="font-size: 10px; padding: 1px 4px; background: #28a745; color: white; border: none; border-radius: 2px;">
Alle
</button>
<select v-if="groups.length > 0"
@change="assignGroupToActivity(item.id, $event.target.value)"
style="font-size: 10px; width: 80px;">
<option value="">Gruppe...</option>
<option v-for="group in groups" :key="group.id" :value="String(group.id)">
{{ group.name }}
</option>
</select>
</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 style="position: relative; display: inline-block;">
<button v-if="!item.isTimeblock" @click="toggleActivityMembers(item)" title="Teilnehmer zuordnen"
class="person-btn">👤</button>
<button v-if="item.isTimeblock" @click="addGroupActivityToTimeblock(item.id)" title="Gruppen-Aktivität hinzufügen"
class="btn-primary" style="font-size: 12px; padding: 2px 6px;">+ Gruppe</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; display: flex; align-items: center; gap: 0.5rem;">
<span>Teilnehmer zuordnen</span>
<button @click="assignAllMembersToActivity(item.id)"
style="font-size: 10px; padding: 1px 4px; background: #28a745; color: white; border: none; border-radius: 2px;">
Alle
</button>
<select v-if="groups.length > 0"
@change="assignGroupToActivity(item.id, $event.target.value)"
style="font-size: 10px; width: 80px;">
<option value="">Gruppe...</option>
<option v-for="group in groups" :key="group.id" :value="String(group.id)">
{{ group.name }}
</option>
</select>
</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>
</div>
</td>
@@ -277,11 +279,12 @@
<td>{{ groupItem.groupsGroupActivity.name }}</td>
<td></td>
<td>
<button @click="toggleGroupActivityMembers(groupItem)" title="Teilnehmer zuordnen"
class="person-btn">👤</button>
<button @click="removeGroupActivity(groupItem.id)" class="trash-btn" title="Löschen">🗑</button>
<div v-if="groupActivityMembersOpenId === groupItem.id" class="dropdown"
style="max-height: 12em; padding: 0.25rem;">
<div style="position: relative; display: inline-block;">
<button @click="toggleGroupActivityMembers(groupItem)" title="Teilnehmer zuordnen"
class="person-btn">👤</button>
<button @click="removeGroupActivity(groupItem.id)" class="trash-btn" title="Löschen">🗑</button>
<div v-if="groupActivityMembersOpenId === groupItem.id" class="dropdown"
style="max-height: 12em; padding: 0.25rem;">
<div style="margin-bottom: 0.25rem; font-weight: 600; display: flex; align-items: center; gap: 0.5rem;">
<span>Teilnehmer zuordnen</span>
<button @click="assignAllMembersToGroupActivity(groupItem.id)"
@@ -307,6 +310,7 @@
<span>{{ m.firstName }} {{ m.lastName }}</span>
</label>
</div>
</div>
</div>
</td>
</tr>
@@ -424,10 +428,11 @@
<div class="participant-actions">
<!-- Gruppenzuordnung für aktive Teilnehmer -->
<select v-if="isParticipant(member.id) && groups.length > 0"
:key="`group-select-${member.id}-${getMemberGroup(member.id)}`"
:value="getMemberGroup(member.id)"
@change="updateMemberGroup(member.id, $event.target.value)"
@input="updateMemberGroup(member.id, $event.target.value)"
style="margin-left: 10px; font-size: 8px; width: 5em;">
class="member-group-select"
style="margin-left: 10px; font-size: 10px; width: 5em; padding: 2px 4px; border: 1px solid #ccc; border-radius: 3px; background-color: white; color: #333;">
<option value="">-</option>
<option v-for="group in groups" :key="group.id" :value="String(group.id)">
{{ group.name }}
@@ -513,10 +518,11 @@
<div class="participant-actions">
<!-- Gruppenzuordnung für aktive Teilnehmer -->
<select v-if="isParticipant(member.id) && groups.length > 0"
:key="`group-select-${member.id}-${getMemberGroup(member.id)}`"
:value="getMemberGroup(member.id)"
@change="updateMemberGroup(member.id, $event.target.value)"
@input="updateMemberGroup(member.id, $event.target.value)"
style="margin-left: 10px; font-size: 8px; width: 5em;">
class="member-group-select"
style="margin-left: 10px; font-size: 10px; width: 5em; padding: 2px 4px; border: 1px solid #ccc; border-radius: 3px; background-color: white; color: #333;">
<option value="">-</option>
<option v-for="group in groups" :key="group.id" :value="String(group.id)">
{{ group.name }}
@@ -2198,12 +2204,29 @@ export default {
},
async toggleActivityMembers(item) {
console.log('🔍 [toggleActivityMembers] Aufgerufen für item.id:', item.id);
console.log('🔍 [toggleActivityMembers] Aktuelle activityMembersOpenId:', this.activityMembersOpenId);
if (this.activityMembersOpenId === item.id) {
console.log('🔍 [toggleActivityMembers] Schließe Dropdown');
this.activityMembersOpenId = null;
return;
}
console.log('🔍 [toggleActivityMembers] Öffne Dropdown für item.id:', item.id);
this.activityMembersOpenId = item.id;
// Verwende $set für Vue 2 Reaktivität
if (this.$set) {
this.$set(this, 'activityMembersOpenId', item.id);
}
await this.ensureActivityMembersLoaded(item.id);
// Force update um sicherzustellen, dass das Dropdown angezeigt wird
await this.$nextTick();
this.$forceUpdate();
console.log('🔍 [toggleActivityMembers] Dropdown sollte jetzt sichtbar sein');
},
async ensureActivityMembersLoaded(activityId) {
@@ -2327,12 +2350,29 @@ export default {
// Group Activity Members Methods
async toggleGroupActivityMembers(groupItem) {
console.log('🔍 [toggleGroupActivityMembers] Aufgerufen für groupItem.id:', groupItem.id);
console.log('🔍 [toggleGroupActivityMembers] Aktuelle groupActivityMembersOpenId:', this.groupActivityMembersOpenId);
if (this.groupActivityMembersOpenId === groupItem.id) {
console.log('🔍 [toggleGroupActivityMembers] Schließe Dropdown');
this.groupActivityMembersOpenId = null;
return;
}
console.log('🔍 [toggleGroupActivityMembers] Öffne Dropdown für groupItem.id:', groupItem.id);
this.groupActivityMembersOpenId = groupItem.id;
// Verwende $set für Vue 2 Reaktivität
if (this.$set) {
this.$set(this, 'groupActivityMembersOpenId', groupItem.id);
}
await this.ensureGroupActivityMembersLoaded(groupItem.id);
// Force update um sicherzustellen, dass das Dropdown angezeigt wird
await this.$nextTick();
this.$forceUpdate();
console.log('🔍 [toggleGroupActivityMembers] Dropdown sollte jetzt sichtbar sein');
},
async ensureGroupActivityMembersLoaded(groupActivityId) {
@@ -2425,8 +2465,14 @@ export default {
// Gruppenzuordnung für Teilnehmer
getMemberGroup(memberId) {
const value = this.memberGroupsMap ? this.memberGroupsMap[memberId] : undefined;
return value !== undefined && value !== null ? String(value) : '';
if (!this.memberGroupsMap) {
return '';
}
const value = this.memberGroupsMap[memberId];
if (value === undefined || value === null) {
return '';
}
return String(value);
},
async updateMemberGroup(memberId, groupId) {
@@ -2605,16 +2651,41 @@ export default {
async handleParticipantUpdated(data) {
// Nur aktualisieren, wenn das aktuelle Datum betroffen ist
if (this.date && this.date !== 'new' && this.date.id === data.dateId) {
if (this.date && this.date !== 'new' && String(this.date.id) === String(data.dateId)) {
console.log('📡 Teilnehmer aktualisiert (Socket):', data);
console.log('📡 [DiaryView] Aktuelle memberGroupsMap vor Update:', JSON.parse(JSON.stringify(this.memberGroupsMap)));
// Aktualisiere groupId in memberGroupsMap
const groupValue = (data.participant.groupId !== null && data.participant.groupId !== undefined)
? String(data.participant.groupId)
: '';
this.memberGroupsMap = {
...this.memberGroupsMap,
[data.participant.memberId]: groupValue
};
console.log('📡 [DiaryView] Setze groupValue für memberId', data.participant.memberId, 'auf:', groupValue);
console.log('📡 [DiaryView] data.participant:', JSON.parse(JSON.stringify(data.participant)));
// Verwende $set für Vue 2 - das ist wichtig für Reaktivität
if (this.$set) {
this.$set(this.memberGroupsMap, data.participant.memberId, groupValue);
console.log('📡 [DiaryView] Verwendet $set für Vue 2');
} else {
// Vue 3: Erstelle neues Objekt für Reaktivität
this.memberGroupsMap = {
...this.memberGroupsMap,
[data.participant.memberId]: groupValue
};
console.log('📡 [DiaryView] Verwendet Spread-Operator für Vue 3');
}
// Warte auf Vue-Update und force dann ein Re-Render
await this.$nextTick();
console.log('📡 [DiaryView] memberGroupsMap nach Update:', JSON.parse(JSON.stringify(this.memberGroupsMap)));
console.log('📡 [DiaryView] getMemberGroup für memberId', data.participant.memberId, ':', this.getMemberGroup(data.participant.memberId));
// Force Vue update um sicherzustellen, dass die UI aktualisiert wird
this.$forceUpdate();
console.log('✅ [DiaryView] UI-Update erzwungen');
} else {
console.log('⚠️ [DiaryView] Datum stimmt nicht überein - Event dateId:', data.dateId, 'Aktuelles Datum:', this.date?.id);
}
},
@@ -3718,6 +3789,29 @@ img {
}
}
/* Member Group Select - Chrome Fix */
.member-group-select {
font-size: 10px !important;
padding: 2px 4px !important;
border: 1px solid #ccc !important;
border-radius: 3px !important;
background-color: white !important;
color: #333 !important;
-webkit-appearance: menulist !important;
appearance: menulist !important;
}
.member-group-select option {
background-color: white !important;
color: #333 !important;
padding: 4px !important;
}
.member-group-select:focus {
outline: 2px solid #007bff !important;
outline-offset: 2px !important;
}
/* Render Container */
.render-container {
display: flex;