Implement deleteGroupActivity endpoint and enhance addGroupActivity functionality
Added a new endpoint to delete group activities in the diaryDateActivityController and corresponding route in diaryDateActivityRoutes. Enhanced the addGroupActivity function to accept an optional timeblockId parameter, allowing for more precise activity management. Updated the DiaryView component to support the removal of group activities, improving user experience and functionality in activity management.
This commit is contained in:
@@ -79,11 +79,23 @@ export const getDiaryDateActivities = async (req, res) => {
|
||||
export const addGroupActivity = async(req, res) => {
|
||||
try {
|
||||
const { authcode: userToken } = req.headers;
|
||||
const { clubId, diaryDateId, groupId, activity } = req.body;
|
||||
const activityItem = await diaryDateActivityService.addGroupActivity(userToken, clubId, diaryDateId, groupId, activity);
|
||||
const { clubId, diaryDateId, groupId, activity, timeblockId } = req.body;
|
||||
const activityItem = await diaryDateActivityService.addGroupActivity(userToken, clubId, diaryDateId, groupId, activity, timeblockId);
|
||||
res.status(201).json(activityItem);
|
||||
} catch (error) {
|
||||
devLog(error);
|
||||
res.status(500).json({ error: 'Error adding group activity' });
|
||||
}
|
||||
}
|
||||
|
||||
export const deleteGroupActivity = async(req, res) => {
|
||||
try {
|
||||
const { authcode: userToken } = req.headers;
|
||||
const { clubId, groupActivityId } = req.params;
|
||||
await diaryDateActivityService.deleteGroupActivity(userToken, clubId, groupActivityId);
|
||||
res.status(200).json({ message: 'Group activity deleted' });
|
||||
} catch (error) {
|
||||
devLog(error);
|
||||
res.status(500).json({ error: 'Error deleting group activity' });
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
updateDiaryDateActivityOrder,
|
||||
getDiaryDateActivities,
|
||||
addGroupActivity,
|
||||
deleteGroupActivity,
|
||||
} from '../controllers/diaryDateActivityController.js';
|
||||
import { authenticate } from '../middleware/authMiddleware.js';
|
||||
|
||||
@@ -14,6 +15,7 @@ const router = express.Router();
|
||||
router.use(authenticate);
|
||||
|
||||
router.post('/group', addGroupActivity);
|
||||
router.delete('/group/:clubId/:groupActivityId', deleteGroupActivity);
|
||||
router.post('/:clubId/', createDiaryDateActivity);
|
||||
router.put('/:clubId/:id/order', updateDiaryDateActivityOrder);
|
||||
router.put('/:clubId/:id', updateDiaryDateActivity);
|
||||
|
||||
@@ -231,16 +231,31 @@ class DiaryDateActivityService {
|
||||
return activitiesWithImages;
|
||||
}
|
||||
|
||||
async addGroupActivity(userToken, clubId, diaryDateId, groupId, activity) {
|
||||
async addGroupActivity(userToken, clubId, diaryDateId, groupId, activity, timeblockId = null) {
|
||||
await checkAccess(userToken, clubId);
|
||||
const diaryDateActivity = await DiaryDateActivity.findOne({
|
||||
where: {
|
||||
diaryDateId,
|
||||
isTimeblock: true,
|
||||
},
|
||||
order: [['order_id', 'DESC']],
|
||||
limit: 1
|
||||
});
|
||||
let diaryDateActivity;
|
||||
|
||||
if (timeblockId) {
|
||||
// Verwende die spezifische Zeitblock-ID, falls angegeben
|
||||
diaryDateActivity = await DiaryDateActivity.findOne({
|
||||
where: {
|
||||
id: timeblockId,
|
||||
diaryDateId,
|
||||
isTimeblock: true,
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Fallback: Verwende den letzten Zeitblock
|
||||
diaryDateActivity = await DiaryDateActivity.findOne({
|
||||
where: {
|
||||
diaryDateId,
|
||||
isTimeblock: true,
|
||||
},
|
||||
order: [['order_id', 'DESC']],
|
||||
limit: 1
|
||||
});
|
||||
}
|
||||
|
||||
if (!diaryDateActivity) {
|
||||
console.error('[DiaryDateActivityService::addGroupActivity] Activity not found');
|
||||
throw new Error('Activity not found');
|
||||
@@ -276,6 +291,15 @@ class DiaryDateActivityService {
|
||||
devLog(activityData);
|
||||
return await GroupActivity.create(activityData);
|
||||
}
|
||||
|
||||
async deleteGroupActivity(userToken, clubId, groupActivityId) {
|
||||
await checkAccess(userToken, clubId);
|
||||
const groupActivity = await GroupActivity.findByPk(groupActivityId);
|
||||
if (!groupActivity) {
|
||||
throw new Error('Group activity not found');
|
||||
}
|
||||
return await groupActivity.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
export default new DiaryDateActivityService();
|
||||
|
||||
@@ -117,8 +117,7 @@
|
||||
class="dropdown" style="max-height: 9.5em;">
|
||||
<div v-for="s in editSearchResults" :key="s.id"
|
||||
@click="chooseEditSuggestion(s, item)">
|
||||
<span v-if="s.code && s.code.trim() !== ''"><strong>[{{
|
||||
s.name }}]</strong> </span>
|
||||
<span v-if="s.code && s.code.trim() !== ''"><strong>[{{ s.code }}]</strong> </span>{{ s.name }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -139,13 +138,28 @@
|
||||
</td>
|
||||
<td>{{ item.groupActivity ? item.groupActivity.name : '' }}</td>
|
||||
<td>
|
||||
{{ item.duration }}<span
|
||||
v-if="item.durationText && item.durationText.trim() !== ''"> ({{
|
||||
item.durationText }})</span>
|
||||
<span v-if="editingActivityId === item.id">
|
||||
<div style="display: flex; gap: 5px; align-items: center;">
|
||||
<input type="text" v-model="editingDurationText"
|
||||
@input="calculateDurationForEdit"
|
||||
@keyup.enter="saveActivityEdit(item)"
|
||||
placeholder="z.B. 2x7" style="width: 80px;" />
|
||||
<input type="number" v-model="editingDuration"
|
||||
@keyup.enter="saveActivityEdit(item)"
|
||||
style="width: 60px;" placeholder="Min" />
|
||||
</div>
|
||||
</span>
|
||||
<span v-else class="clickable" @click="startActivityEdit(item)">
|
||||
{{ item.duration || '' }}<span
|
||||
v-if="item.durationText && item.durationText.trim() !== ''"> ({{
|
||||
item.durationText }})</span>
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<button v-if="!item.isTimeblock" @click="toggleActivityMembers(item)" title="Teilnehmer zuordnen"
|
||||
class="person-btn">👤</button>
|
||||
<button v-if="item.isTimeblock" @click="addGroupActivityToTimeblock(item.id)" title="Gruppen-Aktivität hinzufügen"
|
||||
class="btn-primary" style="font-size: 12px; padding: 2px 6px;">+ Gruppe</button>
|
||||
<button @click="removePlanItem(item.id)" class="trash-btn">🗑️</button>
|
||||
<div v-if="activityMembersOpenId === item.id" class="dropdown"
|
||||
style="max-height: 12em; padding: 0.25rem;">
|
||||
@@ -177,6 +191,35 @@
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- Formular zum Hinzufügen einer Gruppen-Aktivität direkt nach dem Zeitblock -->
|
||||
<tr v-if="item.isTimeblock && addNewGroupActivity && selectedTimeblockId === item.id">
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td colspan="2">
|
||||
<div style="display: flex; gap: 0.5rem; align-items: center; padding: 0.5rem; background: #f0f0f0; border-radius: 4px;">
|
||||
<select v-model="newPlanItem.groupId" style="flex: 1;">
|
||||
<option value="">Gruppe auswählen...</option>
|
||||
<option v-for="group in groups" :key="group.id" :value="group.id">{{
|
||||
group.name }}</option>
|
||||
</select>
|
||||
<input type="text" v-model="newPlanItem.activity" placeholder="Aktivität"
|
||||
@input="onNewItemInputChange" style="flex: 1;" />
|
||||
<div v-if="newItemShowDropdown && newItemSearchResults.length"
|
||||
class="dropdown" style="max-height: 9.5em;">
|
||||
<div v-for="s in newItemSearchResults" :key="s.id"
|
||||
@click="chooseNewItemSuggestion(s)">
|
||||
<span v-if="s.code && s.code.trim() !== ''"><strong>[{{ s.code
|
||||
}}]</strong> </span>{{ s.name }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td></td>
|
||||
<td>
|
||||
<span class="add-plan-item" @click="addPlanItem">+</span>
|
||||
<span class="cancel" @click="cancelAddItem">X</span>
|
||||
</td>
|
||||
</tr>
|
||||
<template v-for="groupItem in item.groupActivities">
|
||||
<tr>
|
||||
<td></td>
|
||||
@@ -201,6 +244,7 @@
|
||||
<td>
|
||||
<button @click="toggleGroupActivityMembers(groupItem)" title="Teilnehmer zuordnen"
|
||||
class="person-btn">👤</button>
|
||||
<button @click="removeGroupActivity(groupItem.id)" class="trash-btn" title="Löschen">🗑️</button>
|
||||
<div v-if="groupActivityMembersOpenId === groupItem.id" class="dropdown"
|
||||
style="max-height: 12em; padding: 0.25rem;">
|
||||
<div style="margin-bottom: 0.25rem; font-weight: 600; display: flex; align-items: center; gap: 0.5rem;">
|
||||
@@ -270,10 +314,23 @@
|
||||
</td>
|
||||
<td v-else-if="addNewTimeblock">Zeitblock</td>
|
||||
<td v-if="addNewGroupActivity" colspan="2">
|
||||
<select v-model="newPlanItem.groupId">
|
||||
<option v-for="group in groups" :key="group.id" :value="group.id">{{
|
||||
group.name }}</option>
|
||||
</select>
|
||||
<div style="display: flex; gap: 0.5rem; align-items: center;">
|
||||
<select v-model="newPlanItem.groupId" style="flex: 1;">
|
||||
<option value="">Gruppe auswählen...</option>
|
||||
<option v-for="group in groups" :key="group.id" :value="group.id">{{
|
||||
group.name }}</option>
|
||||
</select>
|
||||
<input type="text" v-model="newPlanItem.activity" placeholder="Aktivität"
|
||||
@input="onNewItemInputChange" style="flex: 1;" />
|
||||
<div v-if="newItemShowDropdown && newItemSearchResults.length"
|
||||
class="dropdown" style="max-height: 9.5em;">
|
||||
<div v-for="s in newItemSearchResults" :key="s.id"
|
||||
@click="chooseNewItemSuggestion(s)">
|
||||
<span v-if="s.code && s.code.trim() !== ''"><strong>[{{ s.code
|
||||
}}]</strong> </span>{{ s.name }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td v-else-if="addNewItem || addNewTimeblock"></td>
|
||||
<td v-if="(addNewItem || addNewTimeblock) && !addNewGroupActivity">
|
||||
@@ -538,6 +595,7 @@ export default {
|
||||
renderModalData: null,
|
||||
groups: [],
|
||||
currentTimeBlockId: null,
|
||||
selectedTimeblockId: null,
|
||||
newGroupCount: 2,
|
||||
addtype: 'activity',
|
||||
addNewItem: false,
|
||||
@@ -573,6 +631,8 @@ export default {
|
||||
editSearchResults: [],
|
||||
editSearchForId: null,
|
||||
editingActivityText: '',
|
||||
editingDuration: null,
|
||||
editingDurationText: '',
|
||||
// Suche für Neue-Item-Eingabe
|
||||
newItemShowDropdown: false,
|
||||
newItemSearchResults: [],
|
||||
@@ -1215,16 +1275,22 @@ export default {
|
||||
orderId: this.trainingPlan.length
|
||||
});
|
||||
} else if (this.addNewGroupActivity) {
|
||||
if (!this.newPlanItem.groupId || !this.newPlanItem.activity) {
|
||||
this.showInfo('Hinweis', 'Bitte wählen Sie eine Gruppe und geben Sie eine Aktivität ein.', '', 'warning');
|
||||
return;
|
||||
}
|
||||
await apiClient.post(`/diary-date-activities/group`, {
|
||||
clubId: this.currentClub,
|
||||
diaryDateId: this.date.id,
|
||||
activity: this.newPlanItem.activity,
|
||||
groupId: this.newPlanItem.groupId,
|
||||
timeblockId: this.selectedTimeblockId,
|
||||
});
|
||||
}
|
||||
this.addNewTimeblock = false;
|
||||
this.addNewItem = false;
|
||||
this.addNewGroupActivity = false;
|
||||
this.selectedTimeblockId = null;
|
||||
this.newPlanItem = { activity: '', duration: '', durationText: '', groupId: '' };
|
||||
this.trainingPlan = await apiClient.get(`/diary-date-activities/${this.currentClub}/${this.date.id}`).then(response => response.data);
|
||||
this.calculateIntermediateTimes();
|
||||
@@ -1354,6 +1420,22 @@ export default {
|
||||
this.newPlanItem = { ...this.newPlanItem, duration: calculatedDuration };
|
||||
}
|
||||
},
|
||||
calculateDurationForEdit() {
|
||||
const input = this.editingDurationText;
|
||||
let calculatedDuration = 0;
|
||||
const multiplyPattern = /(\d+)\s*[x*]\s*(\d+)/i;
|
||||
const match = input.match(multiplyPattern);
|
||||
if (match) {
|
||||
const [, num1, num2] = match;
|
||||
calculatedDuration = parseInt(num1) * parseInt(num2);
|
||||
} else if (!isNaN(input) && input.trim() !== '') {
|
||||
calculatedDuration = parseInt(input);
|
||||
}
|
||||
if (calculatedDuration > 0) {
|
||||
calculatedDuration = Math.ceil(calculatedDuration / 5) * 5;
|
||||
this.editingDuration = calculatedDuration;
|
||||
}
|
||||
},
|
||||
async removePlanItem(planItemId) {
|
||||
try {
|
||||
await apiClient.delete(`/diary-date-activities/${this.currentClub}/${planItemId}`);
|
||||
@@ -1362,6 +1444,23 @@ export default {
|
||||
this.showInfo('Fehler', 'Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut.', '', 'error');
|
||||
}
|
||||
},
|
||||
async removeGroupActivity(groupActivityId) {
|
||||
try {
|
||||
await apiClient.delete(`/diary-date-activities/group/${this.currentClub}/${groupActivityId}`);
|
||||
// Entferne die GroupActivity aus dem trainingPlan
|
||||
for (const item of this.trainingPlan) {
|
||||
if (item.groupActivities) {
|
||||
const index = item.groupActivities.findIndex(ga => ga.id === groupActivityId);
|
||||
if (index !== -1) {
|
||||
item.groupActivities.splice(index, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
this.showInfo('Fehler', 'Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut.', '', 'error');
|
||||
}
|
||||
},
|
||||
async onDragEnd(evt) {
|
||||
const movedItem = this.trainingPlan[evt.oldIndex];
|
||||
try {
|
||||
@@ -1502,6 +1601,7 @@ export default {
|
||||
this.addNewItem = false;
|
||||
this.addNewGroupActivity = false;
|
||||
this.addNewTimeblock = false;
|
||||
this.selectedTimeblockId = null;
|
||||
},
|
||||
addTimeblock() {
|
||||
this.addNewTimeblock = true;
|
||||
@@ -1512,6 +1612,13 @@ export default {
|
||||
this.addNewGroupActivity = true;
|
||||
this.addNewItem = false;
|
||||
this.addNewTimeblock = false;
|
||||
this.selectedTimeblockId = null; // Verwende den letzten Zeitblock
|
||||
},
|
||||
addGroupActivityToTimeblock(timeblockId) {
|
||||
this.addNewGroupActivity = true;
|
||||
this.addNewItem = false;
|
||||
this.addNewTimeblock = false;
|
||||
this.selectedTimeblockId = timeblockId;
|
||||
},
|
||||
toggleShowGeneralData() {
|
||||
this.showGeneralData = !this.showGeneralData;
|
||||
@@ -1708,17 +1815,23 @@ export default {
|
||||
startActivityEdit(item) {
|
||||
this.editingActivityId = item.id;
|
||||
this.$nextTick(() => {
|
||||
this.$refs.activityInput.focus();
|
||||
if (this.$refs.activityInput && this.$refs.activityInput[0]) {
|
||||
this.$refs.activityInput[0].focus();
|
||||
} else if (this.$refs.activityInput) {
|
||||
this.$refs.activityInput.focus();
|
||||
}
|
||||
});
|
||||
this.editingActivityText = item.predefinedActivity ? item.predefinedActivity.name : item.activity || '';
|
||||
this.editingDuration = item.duration || null;
|
||||
this.editingDurationText = item.durationText || '';
|
||||
},
|
||||
|
||||
async saveActivityEdit(item) {
|
||||
try {
|
||||
await apiClient.put(`/diary-date-activities/${this.currentClub}/${item.id}`, {
|
||||
customActivityName: this.editingActivityText,
|
||||
duration: item.duration,
|
||||
durationText: item.durationText,
|
||||
duration: this.editingDuration || null,
|
||||
durationText: this.editingDurationText || null,
|
||||
groupId: item.groupId,
|
||||
});
|
||||
|
||||
@@ -1727,6 +1840,8 @@ export default {
|
||||
|
||||
this.editingActivityId = null;
|
||||
this.editingActivityText = '';
|
||||
this.editingDuration = null;
|
||||
this.editingDurationText = '';
|
||||
} catch (error) {
|
||||
this.showInfo('Fehler', 'Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut.', '', 'error');
|
||||
}
|
||||
@@ -1839,6 +1954,9 @@ export default {
|
||||
|
||||
cancelActivityEdit() {
|
||||
this.editingActivityId = null;
|
||||
this.editingActivityText = '';
|
||||
this.editingDuration = null;
|
||||
this.editingDurationText = '';
|
||||
},
|
||||
toggleActivitiesBox() {
|
||||
this.showActivitiesBox = !this.showActivitiesBox;
|
||||
|
||||
Reference in New Issue
Block a user