feat: Optimize activity ordering and enhance plan item movement functionality
All checks were successful
Deploy tt-tagebuch / deploy (push) Successful in 45s

This commit is contained in:
Torsten Schulz (local)
2026-05-22 13:48:43 +02:00
parent 3814d9f178
commit 0f946e9514
2 changed files with 86 additions and 19 deletions

View File

@@ -64,17 +64,8 @@ class DiaryDateActivityService {
}
}
const maxOrderWhere = restData.groupId
? {
diaryDateId: data.diaryDateId,
groupId: restData.groupId
}
: {
// Globale Aktivitäten bei "Alle" sollen am Ende des gesamten Tagesplans landen.
diaryDateId: data.diaryDateId
};
const maxOrderId = await DiaryDateActivity.max('orderId', {
where: maxOrderWhere
where: { diaryDateId: data.diaryDateId }
});
const newOrderId = maxOrderId !== null ? maxOrderId + 1 : 1;
restData.orderId = newOrderId;
@@ -125,6 +116,12 @@ class DiaryDateActivityService {
}
}
for (const key of Object.keys(data)) {
if (data[key] === undefined) {
delete data[key];
}
}
return await activity.update(data);
}
@@ -145,14 +142,12 @@ class DiaryDateActivityService {
throw new Error('Activity not found');
}
const currentOrderId = activity.orderId;
const scopeGroupId = activity.groupId || null;
if (newOrderId < currentOrderId) {
await DiaryDateActivity.increment(
{ orderId: 1 },
{
where: {
diaryDateId: activity.diaryDateId,
groupId: scopeGroupId,
orderId: { [Op.gte]: newOrderId, [Op.lt]: currentOrderId },
},
}
@@ -163,7 +158,6 @@ class DiaryDateActivityService {
{
where: {
diaryDateId: activity.diaryDateId,
groupId: scopeGroupId,
orderId: { [Op.lte]: newOrderId, [Op.gt]: currentOrderId },
},
}
@@ -308,10 +302,7 @@ class DiaryDateActivityService {
devLog(predefinedActivity);
const maxOrderId = await DiaryDateActivity.max('orderId', {
where: {
diaryDateId,
groupId: Number(groupId)
}
where: { diaryDateId }
});
const nextOrderId = Number.isFinite(maxOrderId) ? (maxOrderId + 1) : 1;

View File

@@ -442,6 +442,18 @@
</div>
<div class="plan-row-actions">
<button @click="startActivityEdit(item)" class="plan-row-action-button">{{ $t('common.edit') }}</button>
<button
@click="movePlanItem(item, 'up')"
class="plan-row-action-button"
:disabled="!canMovePlanItem(item, 'up')"
title="Nach oben"
></button>
<button
@click="movePlanItem(item, 'down')"
class="plan-row-action-button"
:disabled="!canMovePlanItem(item, 'down')"
title="Nach unten"
></button>
<button v-if="!isStructuralPlanItem(item)" @click="toggleActivityMembers(item)" class="plan-row-action-button">{{ $t('diary.assignShort') }}</button>
<button @click="removePlanItem(item.id)" class="plan-row-action-button plan-row-action-button-danger">{{ $t('common.delete') }}</button>
</div>
@@ -460,6 +472,18 @@
</div>
<div class="plan-row-actions">
<button @click="startActivityEdit(item)" class="plan-row-action-button">{{ $t('common.edit') }}</button>
<button
@click="movePlanItem(item, 'up')"
class="plan-row-action-button"
:disabled="!canMovePlanItem(item, 'up')"
title="Nach oben"
></button>
<button
@click="movePlanItem(item, 'down')"
class="plan-row-action-button"
:disabled="!canMovePlanItem(item, 'down')"
title="Nach unten"
></button>
<button v-if="!isStructuralPlanItem(item)" @click="toggleActivityMembers(item)" class="plan-row-action-button">{{ $t('diary.assignShort') }}</button>
<button @click="removePlanItem(item.id)" class="plan-row-action-button plan-row-action-button-danger">{{ $t('common.delete') }}</button>
</div>
@@ -2762,16 +2786,68 @@ export default {
this.showInfo(this.$t('messages.error'), this.$t('diary.errorOccurred'), '', 'error');
}
},
getOrderedPlanItems() {
return (Array.isArray(this.trainingPlan) ? this.trainingPlan : [])
.filter(item => item && !item.isTimeblock)
.slice()
.sort((a, b) => {
const orderA = Number.isFinite(Number(a?.orderId)) ? Number(a.orderId) : Number.MAX_SAFE_INTEGER;
const orderB = Number.isFinite(Number(b?.orderId)) ? Number(b.orderId) : Number.MAX_SAFE_INTEGER;
if (orderA !== orderB) return orderA - orderB;
return Number(a?.id || 0) - Number(b?.id || 0);
});
},
canMovePlanItem(item, direction) {
const orderedItems = this.getOrderedPlanItems();
const index = orderedItems.findIndex(entry => Number(entry?.id) === Number(item?.id));
if (index < 0) return false;
if (direction === 'up') return index > 0;
if (direction === 'down') return index < orderedItems.length - 1;
return false;
},
async movePlanItem(item, direction) {
if (!this.canMovePlanItem(item, direction)) {
return;
}
const orderedItems = this.getOrderedPlanItems();
const index = orderedItems.findIndex(entry => Number(entry?.id) === Number(item?.id));
const targetIndex = direction === 'up' ? index - 1 : index + 1;
const targetItem = orderedItems[targetIndex];
if (!targetItem) return;
const currentOrder = Number.isFinite(Number(item.orderId)) ? Number(item.orderId) : index + 1;
const targetOrder = Number.isFinite(Number(targetItem.orderId)) ? Number(targetItem.orderId) : targetIndex + 1;
try {
await apiClient.put(`/diary-date-activities/${this.currentClub}/${item.id}`, {
orderId: targetOrder
});
await apiClient.put(`/diary-date-activities/${this.currentClub}/${targetItem.id}`, {
orderId: currentOrder
});
await this.loadTrainingPlan();
} catch (error) {
this.showInfo(this.$t('messages.error'), this.$t('diary.errorOccurred'), '', 'error');
}
},
async onDragEnd(evt) {
const oldIndex = Number.isInteger(evt.oldDraggableIndex) ? evt.oldDraggableIndex : evt.oldIndex;
const newIndex = Number.isInteger(evt.newDraggableIndex) ? evt.newDraggableIndex : evt.newIndex;
const movedItem = this.trainingPlan[oldIndex];
const visibleItems = this.filteredTrainingPlan.slice();
const movedId = evt?.item?.dataset?.planId;
const movedItem = movedId
? this.trainingPlan.find(item => Number(item.id) === Number(movedId))
: visibleItems[oldIndex];
if (!movedItem) {
return;
}
const targetItem = visibleItems[newIndex];
const targetOrder = targetItem && Number(targetItem.id) !== Number(movedItem.id)
? Number(targetItem.orderId)
: newIndex + 1;
try {
await apiClient.put(`/diary-date-activities/${this.currentClub}/${movedItem.id}/order`, {
orderId: newIndex + 1
orderId: targetOrder
});
await this.loadTrainingPlan();
} catch (error) {