From 5707c929d495012dc0d600e148746ce0d6796b94 Mon Sep 17 00:00:00 2001 From: Torsten Schulz Date: Wed, 4 Sep 2024 10:46:39 +0200 Subject: [PATCH] Some changes --- .gitignore | 6 +- backend/controllers/activityController.js | 21 ++++ backend/controllers/diaryController.js | 4 +- backend/controllers/participantController.js | 34 +++++ backend/models/Activity.js | 30 +++++ backend/models/Participant.js | 39 ++++++ backend/models/User.js | 4 +- backend/models/index.js | 10 ++ backend/routes/activityRoutes.js | 10 ++ backend/routes/participantRoutes.js | 11 ++ backend/server.js | 16 ++- frontend/src/views/DiaryView.vue | 125 +++++++++++++++++-- 12 files changed, 287 insertions(+), 23 deletions(-) create mode 100644 backend/controllers/activityController.js create mode 100644 backend/controllers/participantController.js create mode 100644 backend/models/Activity.js create mode 100644 backend/models/Participant.js create mode 100644 backend/routes/activityRoutes.js create mode 100644 backend/routes/participantRoutes.js diff --git a/.gitignore b/.gitignore index a152627..a89d92d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,7 @@ node_modules frontend/node_modules -backend/node_modules \ No newline at end of file +backend/node_modules +.env +frontend/.env +backend/.env + diff --git a/backend/controllers/activityController.js b/backend/controllers/activityController.js new file mode 100644 index 0000000..905c232 --- /dev/null +++ b/backend/controllers/activityController.js @@ -0,0 +1,21 @@ +import Activity from '../models/Activity.js'; + +export const addActivity = async (req, res) => { + try { + const { diaryDateId, description } = req.body; + const activity = await Activity.create({ diaryDateId, description }); + res.status(201).json(activity); + } catch (error) { + res.status(500).json({ error: 'Fehler beim Hinzufügen der Aktivität' }); + } +}; + +export const getActivities = async (req, res) => { + try { + const { diaryDateId } = req.params; + const activities = await Activity.findAll({ where: { diaryDateId } }); + res.status(200).json(activities); + } catch (error) { + res.status(500).json({ error: 'Fehler beim Abrufen der Aktivitäten' }); + } +}; diff --git a/backend/controllers/diaryController.js b/backend/controllers/diaryController.js index d65c084..fcdc23f 100644 --- a/backend/controllers/diaryController.js +++ b/backend/controllers/diaryController.js @@ -40,8 +40,8 @@ const updateTrainingTimes = async (req, res) => { const { clubId } = req.params; const { authcode: userToken } = req.headers; const { date, trainingStart, trainingEnd } = req.body; - if (!date || !trainingStart || !trainingEnd) { - throw new HttpError('All fields (date, trainingStart, trainingEnd) are required', 400); + if (!date || !trainingStart) { + throw new HttpError('notallfieldsfilled', 400); } const updatedDate = await diaryService.updateTrainingTimes(userToken, clubId, date, trainingStart, trainingEnd); res.status(200).json(updatedDate); diff --git a/backend/controllers/participantController.js b/backend/controllers/participantController.js new file mode 100644 index 0000000..c898908 --- /dev/null +++ b/backend/controllers/participantController.js @@ -0,0 +1,34 @@ +import Participant from '../models/Participant.js'; + +export const getParticipants = async (req, res) => { + try { + const { dateId } = req.params; + const participants = await Participant.findAll({ where: { diaryDateId: dateId } }); + res.status(200).json(participants); + } catch (error) { + console.log(error); + res.status(500).json({ error: 'Fehler beim Abrufen der Teilnehmer' }); + } +}; + +export const addParticipant = async (req, res) => { + try { + const { diaryDateId, memberId } = req.body; + const participant = await Participant.create({ diaryDateId, memberId }); + res.status(201).json(participant); + } catch (error) { + console.log(error); + res.status(500).json({ error: 'Fehler beim Hinzufügen des Teilnehmers' }); + } +}; + +export const removeParticipant = async (req, res) => { + try { + const { diaryDateId, memberId } = req.body; + await Participant.destroy({ where: { diaryDateId, memberId } }); + res.status(200).json({ message: 'Teilnehmer entfernt' }); + } catch (error) { + console.log(error); + res.status(500).json({ error: 'Fehler beim Entfernen des Teilnehmers' }); + } +}; diff --git a/backend/models/Activity.js b/backend/models/Activity.js new file mode 100644 index 0000000..90d7ab6 --- /dev/null +++ b/backend/models/Activity.js @@ -0,0 +1,30 @@ +import { DataTypes } from 'sequelize'; +import sequelize from '../database.js'; +import DiaryDate from './DiaryDates.js'; + +const Activity = sequelize.define('Activity', { + id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true, + allowNull: false + }, + description: { + type: DataTypes.TEXT('long'), + allowNull: false + }, + diaryDateId: { + type: DataTypes.INTEGER, + allowNull: false, + references: { + model: DiaryDate, + key: 'id' + } + } +}, { + underscored: true, + tableName: 'activities', + timestamps: true +}); + +export default Activity; diff --git a/backend/models/Participant.js b/backend/models/Participant.js new file mode 100644 index 0000000..de50e3b --- /dev/null +++ b/backend/models/Participant.js @@ -0,0 +1,39 @@ +import { DataTypes } from 'sequelize'; +import sequelize from '../database.js'; +import Member from './Member.js'; +import DiaryDate from './DiaryDates.js'; + +const Participant = sequelize.define('Participant', { + id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true, + allowNull: false + }, + diaryDateId: { + type: DataTypes.INTEGER, + allowNull: false, + references: { + model: DiaryDate, + key: 'id' + } + }, + memberId: { + type: DataTypes.INTEGER, + allowNull: false, + references: { + model: Member, + key: 'id' + } + }, + notes: { + type: DataTypes.STRING(4096), + allowNull: true, + } +}, { + underscored: true, + tableName: 'participants', + timestamps: true +}); + +export default Participant; diff --git a/backend/models/User.js b/backend/models/User.js index 44fd973..da09f19 100644 --- a/backend/models/User.js +++ b/backend/models/User.js @@ -46,7 +46,6 @@ const User = sequelize.define('User', { beforeCreate: async (user) => { const salt = await bcrypt.genSalt(10); user.salt = salt; - console.log(user); user.password = await bcrypt.hash(user.password, salt); }, beforeUpdate: async (user) => { @@ -58,8 +57,7 @@ const User = sequelize.define('User', { } }, afterCreate: async (user) => { - const salt = await bcrypt.genSalt(10); - user.hashedId = await bcrypt.hash(user.id.toString(), salt); + user.hashedId = await bcrypt.hash(user.id.toString(), user.salt); await user.save(); } }, diff --git a/backend/models/index.js b/backend/models/index.js index 7e7b66a..44acd43 100644 --- a/backend/models/index.js +++ b/backend/models/index.js @@ -3,6 +3,9 @@ import Log from './Log.js'; import Club from './Club.js'; import UserClub from './UserClub.js'; import DiaryDate from './DiaryDates.js'; +import Participant from './Participant.js'; +import Member from './Member.js'; +import Activity from './Activity.js'; User.hasMany(Log, { foreignKey: 'userId' }); Log.belongsTo(User, { foreignKey: 'userId' }); @@ -13,4 +16,11 @@ Club.belongsToMany(User, { through: UserClub, foreignKey: 'clubId' }); DiaryDate.belongsTo(Club, { foreignKey: 'clubId' }); Club.hasMany(DiaryDate, { foreignKey: 'clubId' }); +DiaryDate.belongsToMany(Member, { through: Participant, as: 'participants' }); +Member.belongsToMany(DiaryDate, { through: Participant, as: 'diaryDates' }); + +DiaryDate.hasMany(Activity, { as: 'activities', foreignKey: 'diaryDateId' }); +Activity.belongsTo(DiaryDate, { as: 'diaryDate', foreignKey: 'diaryDateId' }); + + export { User, Log, Club, UserClub }; diff --git a/backend/routes/activityRoutes.js b/backend/routes/activityRoutes.js new file mode 100644 index 0000000..595ae2c --- /dev/null +++ b/backend/routes/activityRoutes.js @@ -0,0 +1,10 @@ +import express from 'express'; +import { addActivity, getActivities } from '../controllers/activityController.js'; +import { authenticate } from '../middleware/authMiddleware.js'; + +const router = express.Router(); + +router.post('/add', authenticate, addActivity); +router.get('/:diaryDateId', authenticate, getActivities); + +export default router; diff --git a/backend/routes/participantRoutes.js b/backend/routes/participantRoutes.js new file mode 100644 index 0000000..96979b7 --- /dev/null +++ b/backend/routes/participantRoutes.js @@ -0,0 +1,11 @@ +import express from 'express'; +import { getParticipants, addParticipant, removeParticipant } from '../controllers/participantController.js'; +import { authenticate } from '../middleware/authMiddleware.js'; + +const router = express.Router(); + +router.get('/:dateId', authenticate, getParticipants); +router.post('/add', authenticate, addParticipant); +router.post('/remove', authenticate, removeParticipant); + +export default router; diff --git a/backend/server.js b/backend/server.js index 005414e..81ab393 100644 --- a/backend/server.js +++ b/backend/server.js @@ -7,8 +7,12 @@ import authRoutes from './routes/authRoutes.js'; import clubRoutes from './routes/clubRoutes.js'; import diaryRoutes from './routes/diaryRoutes.js'; import memberRoutes from './routes/memberRoutes.js'; +import participantRoutes from './routes/participantRoutes.js'; +import activityRoutes from './routes/activityRoutes.js'; import Member from './models/Member.js'; import DiaryDate from './models/DiaryDates.js'; +import Participant from './models/Participant.js'; +import Activity from './models/Activity.js'; const app = express(); const port = process.env.PORT || 3000; @@ -21,6 +25,8 @@ app.use('/api/auth', authRoutes); app.use('/api/clubs', clubRoutes); app.use('/api/clubmembers', memberRoutes); app.use('/api/diary', diaryRoutes); +app.use('/api/participants', participantRoutes); +app.use('/api/activities', activityRoutes); app.use(express.static(path.join(__dirname, '../frontend/dist'))); @@ -31,12 +37,14 @@ app.get('*', (req, res) => { (async () => { try { await sequelize.authenticate(); - await User.sync({ alter: true }); - await Club.sync({ alter: true }); - await UserClub.sync({ alter: true }); - await Log.sync({ alter: true }); + await User.sync({ alter: true }); + await Club.sync({ alter: true }); + await UserClub.sync({ alter: true }); + await Log.sync({ alter: true }); await Member.sync({ alter: true }); await DiaryDate.sync({ alter: true }); + await Participant.sync({ alter: true }); + await Activity.sync({ alter: true }); app.listen(port, () => { console.log(`Server is running on http://localhost:${port}`); }); diff --git a/frontend/src/views/DiaryView.vue b/frontend/src/views/DiaryView.vue index ab355a5..ae6990e 100644 --- a/frontend/src/views/DiaryView.vue +++ b/frontend/src/views/DiaryView.vue @@ -5,7 +5,7 @@ @@ -42,6 +42,34 @@ + + +
+
+
+

Teilnehmer

+
    +
  • + +
  • +
+
+
+

Aktivitäten

+ + +
    +
  • + {{ activity.description }} +
  • +
+
+
+
@@ -59,6 +87,10 @@ export default { newDate: '', trainingStart: '', trainingEnd: '', + members: [], + participants: [], + newActivity: '', + activities: [], }; }, computed: { @@ -68,28 +100,31 @@ export default { async init() { if (this.isAuthenticated && this.currentClub) { const response = await apiClient.get(`/diary/${this.currentClub}`); - this.dates = response.data.map(entry => entry.date); + this.dates = response.data.map(entry => ({ id: entry.id, date: entry.date })); } }, async handleDateChange() { this.showForm = this.date === 'new'; if (this.date && this.date !== 'new') { - const selectedDate = this.dates.find(d => d === this.date); - if (selectedDate) { - const response = await apiClient.get(`/diary/${this.currentClub}`); - const dateData = response.data.find(entry => entry.date === this.date); - this.trainingStart = dateData.trainingStart; - this.trainingEnd = dateData.trainingEnd; - } + const dateId = this.date.id; + const response = await apiClient.get(`/diary/${this.currentClub}`); + const dateData = response.data.find(entry => entry.id === dateId); + this.trainingStart = dateData.trainingStart; + this.trainingEnd = dateData.trainingEnd; + + await this.loadMembers(); + await this.loadParticipants(dateId); + await this.loadActivities(dateId); } else { this.newDate = ''; this.trainingStart = ''; this.trainingEnd = ''; + this.participants = []; } }, setCurrentDate() { const today = new Date().toISOString().split('T')[0]; - this.newDate = today; + this.newDate = today; }, async createDate() { try { @@ -98,8 +133,8 @@ export default { trainingStart: this.trainingStart || null, trainingEnd: this.trainingEnd || null, }); - this.dates.push(response.data.date); - this.date = response.data.date; + this.dates.push({ id: response.data.id, date: response.data.date }); + this.date = { id: response.data.id, date: response.data.date }; this.showForm = false; this.newDate = ''; this.trainingStart = ''; @@ -112,8 +147,9 @@ export default { }, async updateTrainingTimes() { try { + const dateId = this.date.id; const response = await apiClient.put(`/diary/${this.currentClub}`, { - date: this.date, + dateId, trainingStart: this.trainingStart || null, trainingEnd: this.trainingEnd || null, }); @@ -124,6 +160,49 @@ export default { alert('Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut.'); } }, + async loadMembers() { + const response = await apiClient.get(`/clubmembers/${this.currentClub}`); + this.members = response.data; + }, + async loadParticipants(dateId) { + const response = await apiClient.get(`/participants/${dateId}`); + this.participants = response.data.map(participant => participant.memberId); + }, + isParticipant(memberId) { + return this.participants.includes(memberId); + }, + async toggleParticipant(memberId) { + const isParticipant = this.isParticipant(memberId); + const dateId = this.date.id; + if (isParticipant) { + await apiClient.post('/participants/remove', { + diaryDateId: dateId, + memberId + }); + this.participants = this.participants.filter(id => id !== memberId); + } else { + await apiClient.post('/participants/add', { + diaryDateId: dateId, + memberId + }); + this.participants.push(memberId); + } + }, + async loadActivities(dateId) { + const response = await apiClient.get(`/activities/${dateId}`); + this.activities = response.data; + }, + async addActivity() { + const dateId = this.date.id; + if (this.newActivity) { + const response = await apiClient.post('/activities/add', { + diaryDateId: dateId, + description: this.newActivity + }); + this.activities.push(response.data); + this.newActivity = ''; + } + } }, async mounted() { await this.init(); @@ -155,4 +234,24 @@ button[type="button"] { button:hover { background-color: #45a049; } + +.columns { + display: flex; + justify-content: space-between; +} + +.column { + flex: 0 0 45%; +} + +textarea { + width: 100%; + margin-bottom: 10px; +} + +ul { + list-style-type: none; + padding: 0; +} +