feat(DiaryDateActivity): enhance group activity management with duration and order features
All checks were successful
Deploy tt-tagebuch / deploy (push) Successful in 43s

- Added `duration`, `durationText`, and `orderId` fields to the GroupActivity model to support detailed activity tracking.
- Updated `addGroupActivity` and `updateGroupActivity` methods in the DiaryDateActivityService to handle new fields, improving activity management capabilities.
- Enhanced the DiaryView component to allow users to input and display duration and duration text for group activities, improving user experience and data clarity.
This commit is contained in:
Torsten Schulz (local)
2026-05-08 11:28:22 +02:00
parent 25c3b90972
commit 940f77e29b
5 changed files with 106 additions and 12 deletions

View File

@@ -122,8 +122,18 @@ export const getDiaryDateActivities = async (req, res) => {
export const addGroupActivity = async(req, res) => {
try {
const { authcode: userToken } = req.headers;
const { clubId, diaryDateId, groupId, activity, predefinedActivityId, timeblockId } = req.body;
const activityItem = await diaryDateActivityService.addGroupActivity(userToken, clubId, diaryDateId, groupId, activity, predefinedActivityId, timeblockId);
const { clubId, diaryDateId, groupId, activity, predefinedActivityId, timeblockId, duration, durationText } = req.body;
const activityItem = await diaryDateActivityService.addGroupActivity(
userToken,
clubId,
diaryDateId,
groupId,
activity,
predefinedActivityId,
timeblockId,
duration,
durationText
);
// Emit Socket-Event
const diaryDate = await DiaryDate.findByPk(diaryDateId);
@@ -142,8 +152,16 @@ export const updateGroupActivity = async(req, res) => {
try {
const { authcode: userToken } = req.headers;
const { clubId, groupActivityId } = req.params;
const { predefinedActivityId } = req.body;
const activityItem = await diaryDateActivityService.updateGroupActivity(userToken, clubId, groupActivityId, predefinedActivityId);
const { predefinedActivityId, duration, durationText, orderId } = req.body;
const activityItem = await diaryDateActivityService.updateGroupActivity(
userToken,
clubId,
groupActivityId,
predefinedActivityId,
duration,
durationText,
orderId
);
// Emit Socket-Event
const GroupActivity = (await import('../models/GroupActivity.js')).default;

View File

@@ -0,0 +1,4 @@
ALTER TABLE `group_activity`
ADD COLUMN `duration` INT NULL AFTER `custom_activity`,
ADD COLUMN `duration_text` VARCHAR(255) NULL AFTER `duration`,
ADD COLUMN `order_id` INT NOT NULL DEFAULT 1 AFTER `duration_text`;

View File

@@ -31,6 +31,19 @@ const GroupActivity = sequelize.define('GroupActivity', {
type: DataTypes.INTEGER,
allowNull: true,
},
duration: {
type: DataTypes.INTEGER,
allowNull: true
},
durationText: {
type: DataTypes.STRING,
allowNull: true
},
orderId: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 1
},
}, {
tableName: 'group_activity',
underscored: true,

View File

@@ -156,8 +156,10 @@ class DiaryDateActivityService {
]
},
{
model: GroupActivity,
model: GroupActivity,
as: 'groupActivities',
separate: true,
order: [['orderId', 'ASC'], ['id', 'ASC']],
include: [
{
model: Group,
@@ -310,7 +312,7 @@ class DiaryDateActivityService {
return activitiesWithImages;
}
async addGroupActivity(userToken, clubId, diaryDateId, groupId, activity, predefinedActivityId = null, timeblockId = null) {
async addGroupActivity(userToken, clubId, diaryDateId, groupId, activity, predefinedActivityId = null, timeblockId = null, duration = null, durationText = null) {
await checkAccess(userToken, clubId);
let diaryDateActivity;
@@ -382,16 +384,33 @@ class DiaryDateActivityService {
}
devLog(predefinedActivity);
const maxOrderId = await GroupActivity.max('orderId', {
where: {
diaryDateActivity: diaryDateActivity.id
}
});
const nextOrderId = Number.isFinite(maxOrderId) ? (maxOrderId + 1) : 1;
const parsedDuration = duration === '' || duration === undefined || duration === null
? null
: parseInt(duration, 10);
const normalizedDurationText = durationText === undefined || durationText === null
? null
: String(durationText).trim() || null;
const activityData = {
diaryDateActivity: diaryDateActivity.id,
groupId: groupId,
customActivity: predefinedActivity.id
customActivity: predefinedActivity.id,
duration: Number.isFinite(parsedDuration) ? parsedDuration : null,
durationText: normalizedDurationText,
orderId: nextOrderId
}
devLog(activityData);
return await GroupActivity.create(activityData);
}
async updateGroupActivity(userToken, clubId, groupActivityId, predefinedActivityId) {
async updateGroupActivity(userToken, clubId, groupActivityId, predefinedActivityId, duration, durationText, orderId) {
await checkAccess(userToken, clubId);
const groupActivity = await GroupActivity.findByPk(groupActivityId);
if (!groupActivity) {
@@ -404,8 +423,25 @@ class DiaryDateActivityService {
throw new Error('Predefined activity not found');
}
// Aktualisiere die customActivity (die auf die PredefinedActivity verweist)
groupActivity.customActivity = predefinedActivityId;
if (predefinedActivityId !== undefined && predefinedActivityId !== null) {
// Aktualisiere die customActivity (die auf die PredefinedActivity verweist)
groupActivity.customActivity = predefinedActivityId;
}
if (duration !== undefined) {
const parsedDuration = duration === '' || duration === null
? null
: parseInt(duration, 10);
groupActivity.duration = Number.isFinite(parsedDuration) ? parsedDuration : null;
}
if (durationText !== undefined) {
groupActivity.durationText = durationText === null ? null : (String(durationText).trim() || null);
}
if (orderId !== undefined) {
const parsedOrderId = parseInt(orderId, 10);
if (Number.isFinite(parsedOrderId) && parsedOrderId > 0) {
groupActivity.orderId = parsedOrderId;
}
}
return await groupActivity.save();
}

View File

@@ -471,7 +471,23 @@
</select>
</div>
</td>
<td></td>
<td>
<div style="display:flex; gap:0.35rem; align-items:center;">
<input
type="text"
v-model="newPlanItem.durationText"
@input="calculateDuration"
:placeholder="$t('diary.durationExampleLong')"
style="width: 120px;"
/>
<input
type="number"
v-model="newPlanItem.duration"
:placeholder="$t('diary.minutes')"
style="width: 72px;"
/>
</div>
</td>
<td>
<span class="add-plan-item" @click="addPlanItem">+</span>
<span class="cancel" @click="cancelAddItem">X</span>
@@ -479,7 +495,12 @@
</tr>
<template v-for="groupItem in item.groupActivities">
<tr class="plan-group-activity-row">
<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></td>
<td>
<div class="plan-group-activity-cell">
@@ -2120,6 +2141,8 @@ export default {
predefinedActivityId: this.newPlanItem.predefinedActivityId,
groupId: Number(this.newPlanItem.groupId),
timeblockId: this.selectedTimeblockId,
duration: this.newPlanItem.duration || null,
durationText: this.newPlanItem.durationText || null
});
}
this.addNewTimeblock = false;