Enhance diary member activity management by adding validation and logging in addMembersToActivity function. Implement checks for participantIds to ensure they are an array, and log relevant information for better debugging. Update DiaryDateActivityService to improve error handling and logging for group activity associations. Modify frontend DiaryView to support group activity member assignment, including new methods for toggling and assigning members to group activities, enhancing user experience and functionality.
This commit is contained in:
@@ -19,20 +19,44 @@ export const addMembersToActivity = async (req, res) => {
|
||||
const { authcode: userToken } = req.headers;
|
||||
const { clubId, diaryDateActivityId } = req.params;
|
||||
const { participantIds } = req.body; // array of participant ids
|
||||
|
||||
console.log('[addMembersToActivity] Request:', {
|
||||
clubId,
|
||||
diaryDateActivityId,
|
||||
participantIds,
|
||||
bodyKeys: Object.keys(req.body)
|
||||
});
|
||||
|
||||
await checkAccess(userToken, clubId);
|
||||
|
||||
if (!participantIds || !Array.isArray(participantIds)) {
|
||||
console.error('[addMembersToActivity] Invalid participantIds:', participantIds);
|
||||
return res.status(400).json({ error: 'participantIds must be an array' });
|
||||
}
|
||||
|
||||
const validParticipants = await Participant.findAll({ where: { id: participantIds } });
|
||||
console.log('[addMembersToActivity] Valid participants found:', validParticipants.length);
|
||||
|
||||
const validIds = new Set(validParticipants.map(p => p.id));
|
||||
const created = [];
|
||||
for (const pid of participantIds) {
|
||||
if (!validIds.has(pid)) continue;
|
||||
if (!validIds.has(pid)) {
|
||||
console.log('[addMembersToActivity] Participant not found:', pid);
|
||||
continue;
|
||||
}
|
||||
const existing = await DiaryMemberActivity.findOne({ where: { diaryDateActivityId, participantId: pid } });
|
||||
if (!existing) {
|
||||
const rec = await DiaryMemberActivity.create({ diaryDateActivityId, participantId: pid });
|
||||
console.log('[addMembersToActivity] Created:', rec.id);
|
||||
created.push(rec);
|
||||
} else {
|
||||
console.log('[addMembersToActivity] Already exists:', pid);
|
||||
}
|
||||
}
|
||||
console.log('[addMembersToActivity] Success, created:', created.length);
|
||||
res.status(201).json(created);
|
||||
} catch (e) {
|
||||
console.error('[addMembersToActivity] Error:', e);
|
||||
res.status(500).json({ error: 'Error adding members to activity' });
|
||||
}
|
||||
};
|
||||
|
||||
@@ -246,8 +246,20 @@ class DiaryDateActivityService {
|
||||
throw new Error('Activity not found');
|
||||
}
|
||||
const group = await Group.findByPk(groupId);
|
||||
if (!group || group.diaryDateId !== diaryDateActivity.diaryDateId) {
|
||||
if (!group) {
|
||||
console.error('[DiaryDateActivityService::addGroupActivity] Group not found:', groupId);
|
||||
throw new Error('Group not found');
|
||||
}
|
||||
|
||||
console.log('[DiaryDateActivityService::addGroupActivity] Group found:', {
|
||||
groupId: group.id,
|
||||
groupDiaryDateId: group.diaryDateId,
|
||||
activityDiaryDateId: diaryDateActivity.diaryDateId
|
||||
});
|
||||
|
||||
if (group.diaryDateId !== diaryDateActivity.diaryDateId) {
|
||||
console.error('[DiaryDateActivityService::addGroupActivity] Group and date don\'t fit');
|
||||
console.error('Group diaryDateId:', group.diaryDateId, 'Activity diaryDateId:', diaryDateActivity.diaryDateId);
|
||||
throw new Error('Group isn\'t related to date');
|
||||
}
|
||||
const [predefinedActivity, created] = await PredefinedActivity.findOrCreate({
|
||||
|
||||
@@ -17,7 +17,7 @@ class SchedulerService {
|
||||
devLog('Scheduler is already running');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
devLog('Starting scheduler service...');
|
||||
|
||||
// Schedule automatic rating updates at 6:00 AM daily
|
||||
|
||||
@@ -1 +1,19 @@
|
||||
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
|
||||
{
|
||||
"name": "Trainingstagebuch",
|
||||
"short_name": "TT-Tagebuch",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/android-chrome-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/android-chrome-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"theme_color": "#2c3e50",
|
||||
"background_color": "#ffffff",
|
||||
"display": "standalone"
|
||||
}
|
||||
|
||||
@@ -144,7 +144,7 @@
|
||||
item.durationText }})</span>
|
||||
</td>
|
||||
<td>
|
||||
<button @click="toggleActivityMembers(item)" title="Teilnehmer zuordnen"
|
||||
<button v-if="!item.isTimeblock" @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"
|
||||
@@ -198,7 +198,38 @@
|
||||
</td>
|
||||
<td>{{ groupItem.groupsGroupActivity.name }}</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td>
|
||||
<button @click="toggleGroupActivityMembers(groupItem)" title="Teilnehmer zuordnen"
|
||||
class="person-btn">👤</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)"
|
||||
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="assignGroupToGroupActivity(groupItem.id, $event.target.value)"
|
||||
style="font-size: 10px; width: 80px;">
|
||||
<option value="">Gruppe...</option>
|
||||
<option v-for="group in groups" :key="group.id" :value="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="isAssignedToGroupActivity(groupItem.id, m.id)"
|
||||
@change="toggleMemberForGroupActivity(groupItem.id, m.id, $event.target.checked)">
|
||||
<span>{{ m.firstName }} {{ m.lastName }}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
</template>
|
||||
@@ -518,6 +549,8 @@ export default {
|
||||
activityMembersMap: {}, // key: activityId, value: Set(participantIds)
|
||||
activityGroupsMap: {}, // key: activityId, value: groupId
|
||||
memberGroupsMap: {}, // key: memberId, value: groupId
|
||||
groupActivityMembersOpenId: null,
|
||||
groupActivityMembersMap: {}, // key: groupActivityId, value: Set(participantIds)
|
||||
// Schnell hinzufügen Dialog
|
||||
showQuickAddDialog: false,
|
||||
newMember: {
|
||||
@@ -1796,6 +1829,103 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
// Group Activity Members Methods
|
||||
async toggleGroupActivityMembers(groupItem) {
|
||||
if (this.groupActivityMembersOpenId === groupItem.id) {
|
||||
this.groupActivityMembersOpenId = null;
|
||||
return;
|
||||
}
|
||||
this.groupActivityMembersOpenId = groupItem.id;
|
||||
await this.ensureGroupActivityMembersLoaded(groupItem.id);
|
||||
},
|
||||
|
||||
async ensureGroupActivityMembersLoaded(groupActivityId) {
|
||||
if (this.groupActivityMembersMap[groupActivityId]) return;
|
||||
try {
|
||||
const r = await apiClient.get(`/diary-member-activities/${this.currentClub}/${groupActivityId}`);
|
||||
const setIds = new Set(r.data.map(x => x.participantId));
|
||||
this.$set ? this.$set(this.groupActivityMembersMap, groupActivityId, setIds) : (this.groupActivityMembersMap[groupActivityId] = setIds);
|
||||
} catch (e) {
|
||||
this.groupActivityMembersMap[groupActivityId] = new Set();
|
||||
}
|
||||
},
|
||||
|
||||
isAssignedToGroupActivity(groupActivityId, memberId) {
|
||||
const participantId = this.participantIdForMember(memberId);
|
||||
const setIds = this.groupActivityMembersMap[groupActivityId];
|
||||
return setIds ? setIds.has(participantId) : false;
|
||||
},
|
||||
|
||||
async toggleMemberForGroupActivity(groupActivityId, 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);
|
||||
}
|
||||
// Zuordnung erstellen
|
||||
await apiClient.post(`/diary-member-activities/${this.currentClub}/${groupActivityId}`, {
|
||||
participantIds: [participantId],
|
||||
});
|
||||
const setIds = this.groupActivityMembersMap[groupActivityId] || new Set();
|
||||
setIds.add(participantId);
|
||||
this.$set ? this.$set(this.groupActivityMembersMap, groupActivityId, setIds) : (this.groupActivityMembersMap[groupActivityId] = setIds);
|
||||
} else {
|
||||
// Zuordnung entfernen
|
||||
await apiClient.delete(`/diary-member-activities/${this.currentClub}/${groupActivityId}/${participantId}`);
|
||||
const setIds = this.groupActivityMembersMap[groupActivityId];
|
||||
if (setIds) {
|
||||
setIds.delete(participantId);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Zuordnen des Teilnehmers zu Gruppen-Aktivität:', error);
|
||||
this.showInfo('Fehler', 'Teilnehmer konnte nicht zugeordnet werden.', '', 'error');
|
||||
}
|
||||
},
|
||||
|
||||
async assignAllMembersToGroupActivity(groupActivityId) {
|
||||
try {
|
||||
// Alle anwesenden Mitglieder zur Gruppen-Aktivität hinzufügen
|
||||
for (const member of this.presentMembers) {
|
||||
if (!this.isAssignedToGroupActivity(groupActivityId, member.id)) {
|
||||
await this.toggleMemberForGroupActivity(groupActivityId, member.id, true);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Zuordnen aller Teilnehmer:', error);
|
||||
this.showInfo('Fehler', 'Fehler beim Zuordnen aller Teilnehmer', '', 'error');
|
||||
}
|
||||
},
|
||||
|
||||
async assignGroupToGroupActivity(groupActivityId, groupId) {
|
||||
if (!groupId) return; // Leere Auswahl ignorieren
|
||||
|
||||
try {
|
||||
// Alle Mitglieder der gewählten Gruppe zur Gruppen-Aktivität hinzufügen
|
||||
const groupMembers = this.presentMembers.filter(member =>
|
||||
this.getMemberGroup(member.id) === groupId
|
||||
);
|
||||
|
||||
for (const member of groupMembers) {
|
||||
if (!this.isAssignedToGroupActivity(groupActivityId, member.id)) {
|
||||
await this.toggleMemberForGroupActivity(groupActivityId, member.id, true);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Zuordnen der Gruppe:', error);
|
||||
this.showInfo('Fehler', 'Fehler beim Zuordnen der Gruppe', '', 'error');
|
||||
}
|
||||
},
|
||||
|
||||
// Gruppenzuordnung für Teilnehmer
|
||||
getMemberGroup(memberId) {
|
||||
return this.memberGroupsMap[memberId] || '';
|
||||
|
||||
Reference in New Issue
Block a user