Some changes

This commit is contained in:
Torsten Schulz
2024-09-04 10:46:39 +02:00
parent 1c053eb491
commit 5707c929d4
12 changed files with 287 additions and 23 deletions

6
.gitignore vendored
View File

@@ -1,3 +1,7 @@
node_modules
frontend/node_modules
backend/node_modules
backend/node_modules
.env
frontend/.env
backend/.env

View 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' });
}
};

View File

@@ -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);

View 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' });
}
};

View 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;

View 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;

View File

@@ -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();
}
},

View File

@@ -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 };

View 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;

View 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;

View File

@@ -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}`);
});

View File

@@ -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>