Some changes
This commit is contained in:
6
.gitignore
vendored
6
.gitignore
vendored
@@ -1,3 +1,7 @@
|
||||
node_modules
|
||||
frontend/node_modules
|
||||
backend/node_modules
|
||||
backend/node_modules
|
||||
.env
|
||||
frontend/.env
|
||||
backend/.env
|
||||
|
||||
|
||||
21
backend/controllers/activityController.js
Normal file
21
backend/controllers/activityController.js
Normal file
@@ -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' });
|
||||
}
|
||||
};
|
||||
@@ -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);
|
||||
|
||||
34
backend/controllers/participantController.js
Normal file
34
backend/controllers/participantController.js
Normal file
@@ -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' });
|
||||
}
|
||||
};
|
||||
30
backend/models/Activity.js
Normal file
30
backend/models/Activity.js
Normal file
@@ -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;
|
||||
39
backend/models/Participant.js
Normal file
39
backend/models/Participant.js
Normal file
@@ -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;
|
||||
@@ -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();
|
||||
}
|
||||
},
|
||||
|
||||
@@ -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 };
|
||||
|
||||
10
backend/routes/activityRoutes.js
Normal file
10
backend/routes/activityRoutes.js
Normal file
@@ -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;
|
||||
11
backend/routes/participantRoutes.js
Normal file
11
backend/routes/participantRoutes.js
Normal file
@@ -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;
|
||||
@@ -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}`);
|
||||
});
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<label>Datum:
|
||||
<select v-model="date" @change="handleDateChange">
|
||||
<option value="new">Neu anlegen</option>
|
||||
<option v-for="date in dates" :key="date" :value="date">{{ date }}</option>
|
||||
<option v-for="entry in dates" :key="entry.id" :value="entry">{{ entry.date }} </option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
@@ -42,6 +42,34 @@
|
||||
<button type="submit">Zeiten aktualisieren</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Teilnehmer und Aktivitäten -->
|
||||
<div v-if="date !== 'new' && date !== null">
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<h3>Teilnehmer</h3>
|
||||
<ul>
|
||||
<li v-for="member in members" :key="member.id">
|
||||
<label>
|
||||
<input type="checkbox" :value="member.id" @change="toggleParticipant(member.id)"
|
||||
:checked="isParticipant(member.id)">
|
||||
{{ member.firstName }} {{ member.lastName }}
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="column">
|
||||
<h3>Aktivitäten</h3>
|
||||
<textarea v-model="newActivity"></textarea>
|
||||
<button @click="addActivity">Aktivität hinzufügen</button>
|
||||
<ul>
|
||||
<li v-for="activity in activities" :key="activity.id">
|
||||
{{ activity.description }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user