diff --git a/backend/controllers/memberActivityController.js b/backend/controllers/memberActivityController.js index 9e5a81e..beb2e65 100644 --- a/backend/controllers/memberActivityController.js +++ b/backend/controllers/memberActivityController.js @@ -99,74 +99,73 @@ export const getMemberActivities = async (req, res) => { // 2. Get all group activities for groups the member belongs to const groupActivities = []; if (memberGroupIds.size > 0) { - const groupActivitiesData = await DiaryDateActivity.findAll({ + // Suche direkt nach GroupActivity-Einträgen für die Gruppen des Members + const groupActivitiesData = await GroupActivity.findAll({ + where: { + groupId: { + [Op.in]: Array.from(memberGroupIds) + } + }, include: [ { - model: DiaryDates, - as: 'diaryDate', - where: startDate ? { - date: { - [Op.gte]: startDate + model: DiaryDateActivity, + as: 'activityGroupActivity', + include: [ + { + model: DiaryDates, + as: 'diaryDate', + where: startDate ? { + date: { + [Op.gte]: startDate + } + } : {} + }, + { + model: PredefinedActivity, + as: 'predefinedActivity', + required: false } - } : {} + ] }, { model: PredefinedActivity, - as: 'predefinedActivity', - required: false // Kann null sein für Gruppen-Aktivitäten - }, - { - model: GroupActivity, - as: 'groupActivities', - where: { - groupId: { - [Op.in]: Array.from(memberGroupIds) - } - }, - required: true, - include: [ - { - model: PredefinedActivity, - as: 'groupPredefinedActivity', - required: false // Kann null sein - } - ] + as: 'groupPredefinedActivity', + required: false } ] }); // Erstelle virtuelle DiaryMemberActivity-Objekte für Gruppen-Aktivitäten - for (const activity of groupActivitiesData) { - // Finde den entsprechenden Participant für diese Aktivität + for (const groupActivity of groupActivitiesData) { + if (!groupActivity.activityGroupActivity || !groupActivity.activityGroupActivity.diaryDate) { + continue; // Überspringe, wenn keine DiaryDateActivity oder kein DiaryDate vorhanden + } + + const activity = groupActivity.activityGroupActivity; const diaryDateId = activity.diaryDateId; - const relevantParticipants = participants.filter(p => p.diaryDateId === diaryDateId); + + // Finde alle relevanten Participants für dieses DiaryDate + const relevantParticipants = participants.filter(p => + p.diaryDateId === diaryDateId && + p.groupId === groupActivity.groupId + ); for (const participant of relevantParticipants) { - // Prüfe, ob die Aktivität für die Gruppe des Participants ist - const activityGroupIds = activity.groupActivities?.map(ga => ga.groupId) || []; - if (participant.groupId !== null && activityGroupIds.includes(participant.groupId)) { - // Für Gruppen-Aktivitäten: Verwende die PredefinedActivity aus GroupActivity - // Falls vorhanden, sonst die aus DiaryDateActivity - const groupActivity = activity.groupActivities?.find(ga => ga.groupId === participant.groupId); - if (groupActivity && groupActivity.groupPredefinedActivity) { - // Erstelle ein modifiziertes Activity-Objekt mit der PredefinedActivity aus GroupActivity - const modifiedActivity = { - ...activity.toJSON(), - predefinedActivity: groupActivity.groupPredefinedActivity - }; - groupActivities.push({ - activity: modifiedActivity, - participant: participant, - id: null // Virtuell, nicht in DB - }); - } else if (activity.predefinedActivity) { - // Fallback: Verwende die PredefinedActivity aus DiaryDateActivity - groupActivities.push({ - activity: activity, - participant: participant, - id: null // Virtuell, nicht in DB - }); - } + // Verwende die PredefinedActivity aus GroupActivity, falls vorhanden + // Sonst die aus DiaryDateActivity + const predefinedActivity = groupActivity.groupPredefinedActivity || activity.predefinedActivity; + + if (predefinedActivity) { + // Erstelle ein modifiziertes Activity-Objekt + const modifiedActivity = { + ...activity.toJSON(), + predefinedActivity: predefinedActivity + }; + groupActivities.push({ + activity: modifiedActivity, + participant: participant, + id: null // Virtuell, nicht in DB + }); } } } @@ -299,71 +298,70 @@ export const getMemberLastParticipations = async (req, res) => { // 2. Get all group activities for groups the member belongs to const groupActivities = []; if (memberGroupIds.size > 0) { - const groupActivitiesData = await DiaryDateActivity.findAll({ + // Suche direkt nach GroupActivity-Einträgen für die Gruppen des Members + const groupActivitiesData = await GroupActivity.findAll({ + where: { + groupId: { + [Op.in]: Array.from(memberGroupIds) + } + }, include: [ { - model: DiaryDates, - as: 'diaryDate' + model: DiaryDateActivity, + as: 'activityGroupActivity', + include: [ + { + model: DiaryDates, + as: 'diaryDate' + }, + { + model: PredefinedActivity, + as: 'predefinedActivity', + required: false + } + ] }, { model: PredefinedActivity, - as: 'predefinedActivity', - required: false // Kann null sein für Gruppen-Aktivitäten - }, - { - model: GroupActivity, - as: 'groupActivities', - where: { - groupId: { - [Op.in]: Array.from(memberGroupIds) - } - }, - required: true, - include: [ - { - model: PredefinedActivity, - as: 'groupPredefinedActivity', - required: false // Kann null sein - } - ] + as: 'groupPredefinedActivity', + required: false } ], - order: [[{ model: DiaryDates, as: 'diaryDate' }, 'date', 'DESC']], + order: [[{ model: DiaryDateActivity, as: 'activityGroupActivity' }, { model: DiaryDates, as: 'diaryDate' }, 'date', 'DESC']], limit: parseInt(limit) * 10 // Get more to filter }); // Erstelle virtuelle DiaryMemberActivity-Objekte für Gruppen-Aktivitäten - for (const activity of groupActivitiesData) { - // Finde den entsprechenden Participant für diese Aktivität + for (const groupActivity of groupActivitiesData) { + if (!groupActivity.activityGroupActivity || !groupActivity.activityGroupActivity.diaryDate) { + continue; // Überspringe, wenn keine DiaryDateActivity oder kein DiaryDate vorhanden + } + + const activity = groupActivity.activityGroupActivity; const diaryDateId = activity.diaryDateId; - const relevantParticipants = participants.filter(p => p.diaryDateId === diaryDateId); + + // Finde alle relevanten Participants für dieses DiaryDate + const relevantParticipants = participants.filter(p => + p.diaryDateId === diaryDateId && + p.groupId === groupActivity.groupId + ); for (const participant of relevantParticipants) { - // Prüfe, ob die Aktivität für die Gruppe des Participants ist - const activityGroupIds = activity.groupActivities?.map(ga => ga.groupId) || []; - if (participant.groupId !== null && activityGroupIds.includes(participant.groupId)) { - // Für Gruppen-Aktivitäten: Verwende die PredefinedActivity aus GroupActivity - // Falls vorhanden, sonst die aus DiaryDateActivity - const groupActivity = activity.groupActivities?.find(ga => ga.groupId === participant.groupId); - if (groupActivity && groupActivity.groupPredefinedActivity) { - // Erstelle ein modifiziertes Activity-Objekt mit der PredefinedActivity aus GroupActivity - const modifiedActivity = { - ...activity.toJSON(), - predefinedActivity: groupActivity.groupPredefinedActivity - }; - groupActivities.push({ - activity: modifiedActivity, - participant: participant, - id: null // Virtuell, nicht in DB - }); - } else if (activity.predefinedActivity) { - // Fallback: Verwende die PredefinedActivity aus DiaryDateActivity - groupActivities.push({ - activity: activity, - participant: participant, - id: null // Virtuell, nicht in DB - }); - } + // Verwende die PredefinedActivity aus GroupActivity, falls vorhanden + // Sonst die aus DiaryDateActivity + const predefinedActivity = groupActivity.groupPredefinedActivity || activity.predefinedActivity; + + if (predefinedActivity) { + // Erstelle ein modifiziertes Activity-Objekt + const modifiedActivity = { + ...activity.toJSON(), + predefinedActivity: predefinedActivity + }; + groupActivities.push({ + activity: modifiedActivity, + participant: participant, + id: null // Virtuell, nicht in DB + }); } } } @@ -392,27 +390,57 @@ export const getMemberLastParticipations = async (req, res) => { // Kombiniere beide Listen const allActivities = [...memberActivities, ...uniqueGroupActivities]; - // Format the results - const participations = allActivities + // Gruppiere nach Datum + const participationsByDate = new Map(); + + allActivities .filter(ma => { if (!ma.activity || !ma.activity.predefinedActivity || !ma.activity.diaryDate || !ma.participant) { return false; } return true; }) + .forEach(ma => { + const date = ma.activity.diaryDate.date; + const diaryDateId = ma.activity.diaryDate.id; + const activityName = ma.activity.predefinedActivity.name; + + if (!participationsByDate.has(date)) { + participationsByDate.set(date, { + date: date, + diaryDateId: diaryDateId, + activities: [] + }); + } + + const dateEntry = participationsByDate.get(date); + // Füge Aktivität nur hinzu, wenn sie noch nicht vorhanden ist (vermeide Duplikate) + if (!dateEntry.activities.find(a => a === activityName)) { + dateEntry.activities.push(activityName); + } + }); + + // Sortiere nach Datum (neueste zuerst) und nehme die letzten N Daten + const sortedDates = Array.from(participationsByDate.values()) .sort((a, b) => { - // Sortiere nach Datum (neueste zuerst) - const dateA = new Date(a.activity.diaryDate.date); - const dateB = new Date(b.activity.diaryDate.date); + const dateA = new Date(a.date); + const dateB = new Date(b.date); return dateB - dateA; }) - .slice(0, parseInt(limit)) // Limit after filtering - .map(ma => ({ - id: ma.id, - activityName: ma.activity.predefinedActivity.name, - date: ma.activity.diaryDate.date, - diaryDateId: ma.activity.diaryDate.id - })); + .slice(0, parseInt(limit)); + + // Formatiere für das Frontend: Flache Liste mit Datum und Aktivität + const participations = []; + sortedDates.forEach(dateEntry => { + dateEntry.activities.forEach(activityName => { + participations.push({ + id: null, // Virtuell + activityName: activityName, + date: dateEntry.date, + diaryDateId: dateEntry.diaryDateId + }); + }); + }); return res.status(200).json(participations); diff --git a/frontend/src/components/MemberActivityStatsDialog.vue b/frontend/src/components/MemberActivityStatsDialog.vue index ccf4005..a36979a 100644 --- a/frontend/src/components/MemberActivityStatsDialog.vue +++ b/frontend/src/components/MemberActivityStatsDialog.vue @@ -10,10 +10,14 @@