feat(DiaryView, DiaryDateActivityService): implement group filtering and enhance activity display
All checks were successful
Deploy tt-tagebuch / deploy (push) Successful in 43s

- Added a group filter dropdown in the DiaryView component to allow users to filter group activities by selected group.
- Updated the DiaryDateActivityService to include groupId in the query for maximum orderId, improving activity management.
- Enhanced the display logic for group activities to reflect the selected filter, improving user experience and data clarity.
This commit is contained in:
Torsten Schulz (local)
2026-05-08 11:35:22 +02:00
parent 940f77e29b
commit 93796cecd6
2 changed files with 116 additions and 11 deletions

View File

@@ -386,7 +386,8 @@ class DiaryDateActivityService {
devLog(predefinedActivity);
const maxOrderId = await GroupActivity.max('orderId', {
where: {
diaryDateActivity: diaryDateActivity.id
diaryDateActivity: diaryDateActivity.id,
groupId: Number(groupId)
}
});
const nextOrderId = Number.isFinite(maxOrderId) ? (maxOrderId + 1) : 1;

View File

@@ -139,6 +139,15 @@
</div>
</div>
<div class="plan-toolbar-actions">
<label class="plan-view-group-filter">
<span>{{ $t('diary.group') }}</span>
<select v-model="planGroupFilter">
<option value="__all__">{{ $t('common.all') }}</option>
<option v-for="group in groups" :key="group.id" :value="String(group.id)">
{{ group.name }}
</option>
</select>
</label>
<button type="button" @click="openNewPlanItem()">{{ $t('diary.overallActivity') }}</button>
<button type="button" @click="addTimeblock()">{{ $t('diary.addTimeblock') }}</button>
<button v-if="parentIsTimeblock()" type="button" @click="addGroupActivity()">{{ $t('diary.addGroupActivity') }}</button>
@@ -423,8 +432,8 @@
</div>
</td>
<td>
<span v-if="item.isTimeblock && item.groupActivities && item.groupActivities.length" class="plan-row-muted">
{{ item.groupActivities.length }} {{ $t('diary.groupsLabel') }}
<span v-if="item.isTimeblock && getVisibleGroupActivities(item).length" class="plan-row-muted">
{{ getVisibleGroupActivities(item).length }} {{ $t('diary.groupsLabel') }}
</span>
<span v-else>{{ item.groupActivity ? item.groupActivity.name : '' }}</span>
</td>
@@ -493,15 +502,10 @@
<span class="cancel" @click="cancelAddItem">X</span>
</td>
</tr>
<template v-for="groupItem in item.groupActivities">
<template v-for="groupItem in getVisibleGroupActivities(item)">
<tr class="plan-group-activity-row">
<td>
<span class="clickable" @click="startGroupActivityEdit(groupItem)">
{{ groupItem.duration || '' }}<span
v-if="groupItem.durationText && groupItem.durationText.trim() !== ''"> ({{ groupItem.durationText }})</span>
</span>
</td>
<td></td>
<td>{{ formatDisplayTime(calculateGroupActivityStartTime(item, groupItem)) }}</td>
<td>
<div class="plan-group-activity-cell">
<span class="plan-type-badge plan-type-badge-group">{{ $t('diary.group') }}</span>
@@ -525,10 +529,27 @@
</div>
</td>
<td>{{ groupItem.groupsGroupActivity.name }}</td>
<td></td>
<td>
<span class="clickable" @click="startGroupActivityEdit(groupItem)">
{{ groupItem.duration || '' }}<span
v-if="groupItem.durationText && groupItem.durationText.trim() !== ''"> ({{ groupItem.durationText }})</span>
</span>
</td>
<td>
<div class="plan-row-actions">
<button @click="startGroupActivityEdit(groupItem)" class="plan-row-action-button">{{ $t('common.edit') }}</button>
<button
@click="moveGroupActivity(item, groupItem, 'up')"
class="plan-row-action-button"
:disabled="!canMoveGroupActivity(item, groupItem, 'up')"
title="Nach oben"
></button>
<button
@click="moveGroupActivity(item, groupItem, 'down')"
class="plan-row-action-button"
:disabled="!canMoveGroupActivity(item, groupItem, 'down')"
title="Nach unten"
></button>
<button v-if="!isStructuralPlanItem(groupItem)" @click="toggleGroupActivityMembers(groupItem)" :title="$t('diary.assignParticipants')"
class="plan-row-action-button">{{ $t('diary.assignShort') }}</button>
<button @click="removeGroupActivity(groupItem.id)" class="plan-row-action-button plan-row-action-button-danger" :title="$t('diary.delete')">{{ $t('common.delete') }}</button>
@@ -895,6 +916,7 @@ export default {
addtype: 'activity',
addNewItem: false,
addNewGroupActivity: false,
planGroupFilter: '__all__',
addNewTimeblock: false,
activeOverviewPanel: 'trainingDay',
editingGroupId: null,
@@ -1701,6 +1723,17 @@ export default {
getParticipantStatus(memberId) {
return this.participantStatusMap[memberId] || '';
},
getVisibleGroupActivities(timeblockItem) {
const entries = Array.isArray(timeblockItem?.groupActivities) ? timeblockItem.groupActivities : [];
if (this.planGroupFilter === '__all__') {
return entries;
}
const selectedGroupId = Number(this.planGroupFilter);
if (!Number.isFinite(selectedGroupId)) {
return entries;
}
return entries.filter((entry) => Number(entry?.groupsGroupActivity?.id) === selectedGroupId);
},
shouldShowGalleryMember(member) {
return this.getParticipantStatus(member.memberId) !== 'excused';
@@ -2263,6 +2296,71 @@ export default {
hours = hours % 24;
return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`;
},
getGroupActivitiesForGroup(timeblockItem, groupId) {
const normalizedGroupId = Number(groupId);
const items = Array.isArray(timeblockItem?.groupActivities) ? timeblockItem.groupActivities : [];
return items
.filter((entry) => Number(entry?.groupsGroupActivity?.id) === normalizedGroupId)
.slice()
.sort((a, b) => {
const orderA = Number.isFinite(Number(a?.orderId)) ? Number(a.orderId) : 0;
const orderB = Number.isFinite(Number(b?.orderId)) ? Number(b.orderId) : 0;
if (orderA !== orderB) return orderA - orderB;
return Number(a?.id || 0) - Number(b?.id || 0);
});
},
calculateGroupActivityStartTime(timeblockItem, groupItem) {
const baseStart = timeblockItem?.startTime || this.trainingStart;
if (!baseStart || !groupItem?.groupsGroupActivity?.id) {
return baseStart || '';
}
const orderedGroupItems = this.getGroupActivitiesForGroup(timeblockItem, groupItem.groupsGroupActivity.id);
let current = baseStart;
for (const item of orderedGroupItems) {
if (Number(item?.id) === Number(groupItem?.id)) {
return current;
}
const dur = Number(item?.duration || 0);
if (dur > 0) {
current = this.addDurationToTime(current, dur);
}
}
return current;
},
canMoveGroupActivity(timeblockItem, groupItem, direction) {
const groupId = groupItem?.groupsGroupActivity?.id;
if (!groupId) return false;
const orderedGroupItems = this.getGroupActivitiesForGroup(timeblockItem, groupId);
const index = orderedGroupItems.findIndex((entry) => Number(entry?.id) === Number(groupItem?.id));
if (index < 0) return false;
if (direction === 'up') return index > 0;
if (direction === 'down') return index < orderedGroupItems.length - 1;
return false;
},
async moveGroupActivity(timeblockItem, groupItem, direction) {
if (!this.canMoveGroupActivity(timeblockItem, groupItem, direction)) {
return;
}
const groupId = groupItem?.groupsGroupActivity?.id;
const orderedGroupItems = this.getGroupActivitiesForGroup(timeblockItem, groupId);
const index = orderedGroupItems.findIndex((entry) => Number(entry?.id) === Number(groupItem?.id));
const targetIndex = direction === 'up' ? index - 1 : index + 1;
const targetItem = orderedGroupItems[targetIndex];
if (!targetItem) return;
try {
const currentOrder = Number(groupItem.orderId || index + 1);
const targetOrder = Number(targetItem.orderId || targetIndex + 1);
await apiClient.put(`/diary-date-activities/group/${this.currentClub}/${groupItem.id}`, {
orderId: targetOrder
});
await apiClient.put(`/diary-date-activities/group/${this.currentClub}/${targetItem.id}`, {
orderId: currentOrder
});
await this.loadTrainingPlan();
} catch (error) {
this.showInfo(this.$t('messages.error'), this.$t('diary.errorOccurred'), '', 'error');
}
},
calculateDuration() {
const input = this.newPlanItem.durationText;
let calculatedDuration = 0;
@@ -2580,12 +2678,18 @@ export default {
this.addNewItem = false;
this.addNewTimeblock = false;
this.selectedTimeblockId = null; // Verwende den letzten Zeitblock
if (this.planGroupFilter !== '__all__') {
this.newPlanItem.groupId = String(this.planGroupFilter);
}
},
addGroupActivityToTimeblock(timeblockId) {
this.addNewGroupActivity = true;
this.addNewItem = false;
this.addNewTimeblock = false;
this.selectedTimeblockId = timeblockId;
if (this.planGroupFilter !== '__all__') {
this.newPlanItem.groupId = String(this.planGroupFilter);
}
},
getFormattedDate(date) {
return (new Date(date)).toLocaleDateString('de-DE', { year: 'numeric', month: '2-digit', day: '2-digit' });