Refactor member activity display to group participations by date
This commit updates the `MemberActivityStatsDialog` component to group member participations by date, enhancing the presentation of activity data. The logic is introduced to aggregate activities under their respective dates, ensuring a clearer and more organized display. Additionally, CSS styles are added to improve the visual hierarchy and user experience when viewing recent participations.
This commit is contained in:
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user