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 }}) -