From b82a80a11d7f89fa2dc01d69742d39b2f7961eb9 Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Thu, 28 Aug 2025 14:43:04 +0200 Subject: [PATCH] =?UTF-8?q?F=C3=BCgt=20Unterst=C3=BCtzung=20f=C3=BCr=20Akt?= =?UTF-8?q?ivit=C3=A4tenmitglieder=20in=20DiaryView.vue=20hinzu.=20Erm?= =?UTF-8?q?=C3=B6glicht=20das=20Zuordnen=20von=20Teilnehmern=20zu=20Aktivi?= =?UTF-8?q?t=C3=A4ten,=20einschlie=C3=9Flich=20der=20Verwaltung=20von=20Te?= =?UTF-8?q?ilnehmern=20=C3=BCber=20das=20Backend.=20Aktualisiert=20die=20D?= =?UTF-8?q?atenbankmodelle=20und=20-routen,=20um=20die=20neuen=20Funktione?= =?UTF-8?q?n=20zu=20unterst=C3=BCtzen.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../diaryMemberActivityController.js | 52 ++++++++++ backend/models/DiaryMemberActivity.js | 26 +++++ backend/models/index.js | 7 ++ backend/routes/diaryMemberActivityRoutes.js | 15 +++ backend/server.js | 5 +- frontend/src/views/DiaryView.vue | 98 ++++++++++++++++++- 6 files changed, 199 insertions(+), 4 deletions(-) create mode 100644 backend/controllers/diaryMemberActivityController.js create mode 100644 backend/models/DiaryMemberActivity.js create mode 100644 backend/routes/diaryMemberActivityRoutes.js diff --git a/backend/controllers/diaryMemberActivityController.js b/backend/controllers/diaryMemberActivityController.js new file mode 100644 index 0000000..b47a3bd --- /dev/null +++ b/backend/controllers/diaryMemberActivityController.js @@ -0,0 +1,52 @@ +import DiaryMemberActivity from '../models/DiaryMemberActivity.js'; +import Participant from '../models/Participant.js'; +import { checkAccess } from '../utils/userUtils.js'; + +export const getMembersForActivity = async (req, res) => { + try { + const { authcode: userToken } = req.headers; + const { clubId, diaryDateActivityId } = req.params; + await checkAccess(userToken, clubId); + const list = await DiaryMemberActivity.findAll({ where: { diaryDateActivityId } }); + res.status(200).json(list); + } catch (e) { + res.status(500).json({ error: 'Error fetching members for activity' }); + } +}; + +export const addMembersToActivity = async (req, res) => { + try { + const { authcode: userToken } = req.headers; + const { clubId, diaryDateActivityId } = req.params; + const { participantIds } = req.body; // array of participant ids + await checkAccess(userToken, clubId); + const validParticipants = await Participant.findAll({ where: { id: participantIds } }); + const validIds = new Set(validParticipants.map(p => p.id)); + const created = []; + for (const pid of participantIds) { + if (!validIds.has(pid)) continue; + const existing = await DiaryMemberActivity.findOne({ where: { diaryDateActivityId, participantId: pid } }); + if (!existing) { + const rec = await DiaryMemberActivity.create({ diaryDateActivityId, participantId: pid }); + created.push(rec); + } + } + res.status(201).json(created); + } catch (e) { + res.status(500).json({ error: 'Error adding members to activity' }); + } +}; + +export const removeMemberFromActivity = async (req, res) => { + try { + const { authcode: userToken } = req.headers; + const { clubId, diaryDateActivityId, participantId } = req.params; + await checkAccess(userToken, clubId); + await DiaryMemberActivity.destroy({ where: { diaryDateActivityId, participantId } }); + res.status(200).json({ ok: true }); + } catch (e) { + res.status(500).json({ error: 'Error removing member from activity' }); + } +}; + + diff --git a/backend/models/DiaryMemberActivity.js b/backend/models/DiaryMemberActivity.js new file mode 100644 index 0000000..2025a37 --- /dev/null +++ b/backend/models/DiaryMemberActivity.js @@ -0,0 +1,26 @@ +import { DataTypes } from 'sequelize'; +import sequelize from '../database.js'; + +const DiaryMemberActivity = sequelize.define('DiaryMemberActivity', { + id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true, + }, + diaryDateActivityId: { + type: DataTypes.INTEGER, + allowNull: false, + }, + participantId: { + type: DataTypes.INTEGER, + allowNull: false, + }, +}, { + tableName: 'diary_member_activities', + timestamps: true, + underscored: true, +}); + +export default DiaryMemberActivity; + + diff --git a/backend/models/index.js b/backend/models/index.js index f870a69..0500b30 100644 --- a/backend/models/index.js +++ b/backend/models/index.js @@ -13,6 +13,7 @@ import DiaryDateTag from './DiaryDateTag.js'; import DiaryMemberNote from './DiaryMemberNote.js'; import DiaryMemberTag from './DiaryMemberTag.js'; import PredefinedActivity from './PredefinedActivity.js'; +import DiaryMemberActivity from './DiaryMemberActivity.js'; import PredefinedActivityImage from './PredefinedActivityImage.js'; import DiaryDateActivity from './DiaryDateActivity.js'; import Match from './Match.js'; @@ -77,6 +78,11 @@ DiaryDateActivity.belongsTo(DiaryDate, { foreignKey: 'diaryDateId', as: 'diaryDa PredefinedActivity.hasMany(DiaryDateActivity, { foreignKey: 'predefinedActivityId', as: 'predefinedActivities' }); DiaryDateActivity.belongsTo(PredefinedActivity, { foreignKey: 'predefinedActivityId', as: 'predefinedActivity' }); +// DiaryMemberActivity links a Participant to a DiaryDateActivity +DiaryMemberActivity.belongsTo(DiaryDateActivity, { foreignKey: 'diaryDateActivityId', as: 'activity' }); +DiaryDateActivity.hasMany(DiaryMemberActivity, { foreignKey: 'diaryDateActivityId', as: 'activityMembers' }); +DiaryMemberActivity.belongsTo(Participant, { foreignKey: 'participantId', as: 'participant' }); +Participant.hasMany(DiaryMemberActivity, { foreignKey: 'participantId', as: 'memberActivities' }); // PredefinedActivity Images PredefinedActivity.hasMany(PredefinedActivityImage, { foreignKey: 'predefinedActivityId', as: 'images' }); PredefinedActivityImage.belongsTo(PredefinedActivity, { foreignKey: 'predefinedActivityId', as: 'predefinedActivity' }); @@ -202,6 +208,7 @@ export { DiaryMemberNote, DiaryMemberTag, PredefinedActivity, + DiaryMemberActivity, PredefinedActivityImage, DiaryDateActivity, Match, diff --git a/backend/routes/diaryMemberActivityRoutes.js b/backend/routes/diaryMemberActivityRoutes.js new file mode 100644 index 0000000..63451e7 --- /dev/null +++ b/backend/routes/diaryMemberActivityRoutes.js @@ -0,0 +1,15 @@ +import express from 'express'; +import { authenticate } from '../middleware/authMiddleware.js'; +import { addMembersToActivity, removeMemberFromActivity, getMembersForActivity } from '../controllers/diaryMemberActivityController.js'; + +const router = express.Router(); + +router.use(authenticate); + +router.get('/:clubId/:diaryDateActivityId', getMembersForActivity); +router.post('/:clubId/:diaryDateActivityId', addMembersToActivity); +router.delete('/:clubId/:diaryDateActivityId/:participantId', removeMemberFromActivity); + +export default router; + + diff --git a/backend/server.js b/backend/server.js index e17301b..bbf68e3 100644 --- a/backend/server.js +++ b/backend/server.js @@ -6,7 +6,7 @@ import cors from 'cors'; import { User, Log, Club, UserClub, Member, DiaryDate, Participant, Activity, MemberNote, DiaryNote, DiaryTag, MemberDiaryTag, DiaryDateTag, DiaryMemberNote, DiaryMemberTag, - PredefinedActivity, PredefinedActivityImage, DiaryDateActivity, Match, League, Team, Group, + PredefinedActivity, PredefinedActivityImage, DiaryDateActivity, DiaryMemberActivity, Match, League, Team, Group, GroupActivity, Tournament, TournamentGroup, TournamentMatch, TournamentResult, TournamentMember, Accident, UserToken } from './models/index.js'; @@ -22,6 +22,7 @@ import diaryNoteRoutes from './routes/diaryNoteRoutes.js'; import diaryMemberRoutes from './routes/diaryMemberRoutes.js'; import predefinedActivityRoutes from './routes/predefinedActivityRoutes.js'; import diaryDateActivityRoutes from './routes/diaryDateActivityRoutes.js'; +import diaryMemberActivityRoutes from './routes/diaryMemberActivityRoutes.js'; import matchRoutes from './routes/matchRoutes.js'; import Season from './models/Season.js'; import Location from './models/Location.js'; @@ -53,6 +54,7 @@ app.use('/api/tags', diaryTagRoutes); app.use('/api/diarymember', diaryMemberRoutes); app.use('/api/predefined-activities', predefinedActivityRoutes); app.use('/api/diary-date-activities', diaryDateActivityRoutes); +app.use('/api/diary-member-activities', diaryMemberActivityRoutes); app.use('/api/matches', matchRoutes); app.use('/api/group', groupRoutes); app.use('/api/diarydatetags', diaryDateTagRoutes); @@ -90,6 +92,7 @@ app.get('*', (req, res) => { await PredefinedActivity.sync({ alter: true }); await PredefinedActivityImage.sync({ alter: true }); await DiaryDateActivity.sync({ alter: true }); + await DiaryMemberActivity.sync({ alter: true }); await Season.sync({ alter: true }); await League.sync({ alter: true }); await Team.sync({ alter: true }); diff --git a/frontend/src/views/DiaryView.vue b/frontend/src/views/DiaryView.vue index d686ae7..4b0bdd1 100644 --- a/frontend/src/views/DiaryView.vue +++ b/frontend/src/views/DiaryView.vue @@ -136,7 +136,19 @@ v-if="item.durationText && item.durationText.trim() !== ''"> ({{ item.durationText }}) - + + + + +