feat(DiaryView, DiaryDateActivityService): implement group filtering and enhance activity display
All checks were successful
Deploy tt-tagebuch / deploy (push) Successful in 43s
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:
@@ -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;
|
||||
|
||||
@@ -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' });
|
||||
|
||||
Reference in New Issue
Block a user