Added dayplanning

This commit is contained in:
Torsten Schulz
2024-09-11 15:44:56 +02:00
parent 28bf98a169
commit a22d2bcfc6
19 changed files with 778 additions and 52 deletions

View File

@@ -0,0 +1,73 @@
import diaryDateActivityService from '../services/diaryDateActivityService.js';
export const createDiaryDateActivity = async (req, res) => {
try {
const { authcode: userToken } = req.headers;
const { clubId } = req.params;
const { diaryDateId, activity, duration, durationText, orderId } = req.body;
const activityItem = await diaryDateActivityService.createActivity(userToken, clubId, {
diaryDateId,
activity,
duration,
durationText,
orderId,
});
res.status(201).json(activityItem);
} catch (error) {
console.log(error);
res.status(500).json({ error: 'Error creating activity' });
}
};
export const updateDiaryDateActivity = async (req, res) => {
try {
const { authcode: userToken } = req.headers;
const { clubId, id } = req.params;
const { predefinedActivityId, customActivityName, duration, durationText, orderId } = req.body;
const updatedActivity = await diaryDateActivityService.updateActivity(userToken, clubId, id, {
predefinedActivityId,
customActivityName,
duration,
durationText,
orderId,
});
res.status(200).json(updatedActivity);
} catch (error) {
res.status(500).json({ error: 'Error updating activity' });
}
};
export const deleteDiaryDateActivity = async (req, res) => {
try {
const { authcode: userToken } = req.headers;
const { clubId, id } = req.params;
await diaryDateActivityService.deleteActivity(userToken, clubId, id);
res.status(200).json({ message: 'Activity deleted' });
} catch (error) {
res.status(500).json({ error: 'Error deleting activity' });
}
};
export const updateDiaryDateActivityOrder = async (req, res) => {
try {
const { authcode: userToken } = req.headers;
const { clubId, id } = req.params;
const { orderId } = req.body;
const updatedActivity = await diaryDateActivityService.updateActivityOrder(userToken, clubId, id, orderId);
res.status(200).json(updatedActivity);
} catch (error) {
console.log(error);
res.status(500).json({ error: 'Error updating activity order' });
}
};
export const getDiaryDateActivities = async (req, res) => {
try {
const { authcode: userToken } = req.headers;
const { clubId, diaryDateId } = req.params;
const activities = await diaryDateActivityService.getActivities(userToken, clubId, diaryDateId);
res.status(200).json(activities);
} catch (error) {
res.status(500).json({ error: 'Error getting activities' });
}
}

View File

@@ -58,8 +58,8 @@ const addMemberTag = async (req, res) => {
const removeMemberNote = async (req, res) => {
try {
const { authcode: userToken } = req.headers;
const { memberId, diaryDateId, content } = req.body;
await DiaryMemberService.removeNoteFromMember(userToken, req.params.clubId, diaryDateId, memberId, content);
const { clubId, noteId } = req.params;
await DiaryMemberService.removeNoteFromMember(userToken, clubId, noteId);
const notes = await DiaryMemberService.getNotesForMember(userToken, req.params.clubId, diaryDateId, memberId);
res.status(200).json(notes);
} catch (error) {

View File

@@ -0,0 +1,48 @@
import predefinedActivityService from '../services/predefinedActivityService.js';
export const createPredefinedActivity = async (req, res) => {
try {
const { name, description, durationText, duration } = req.body;
const predefinedActivity = await predefinedActivityService.createPredefinedActivity({ name, description, durationText, duration });
res.status(201).json(predefinedActivity);
} catch (error) {
console.error('[createPredefinedActivity] - Error:', error);
res.status(500).json({ error: 'Error creating predefined activity' });
}
};
export const getAllPredefinedActivities = async (req, res) => {
try {
const predefinedActivities = await predefinedActivityService.getAllPredefinedActivities();
res.status(200).json(predefinedActivities);
} catch (error) {
console.error('[getAllPredefinedActivities] - Error:', error);
res.status(500).json({ error: 'Error fetching predefined activities' });
}
};
export const getPredefinedActivityById = async (req, res) => {
try {
const { id } = req.params;
const predefinedActivity = await predefinedActivityService.getPredefinedActivityById(id);
if (!predefinedActivity) {
return res.status(404).json({ error: 'Predefined activity not found' });
}
res.status(200).json(predefinedActivity);
} catch (error) {
console.error('[getPredefinedActivityById] - Error:', error);
res.status(500).json({ error: 'Error fetching predefined activity' });
}
};
export const updatePredefinedActivity = async (req, res) => {
try {
const { id } = req.params;
const { name, description, durationText, duration } = req.body;
const updatedActivity = await predefinedActivityService.updatePredefinedActivity(id, { name, description, durationText, duration });
res.status(200).json(updatedActivity);
} catch (error) {
console.error('[updatePredefinedActivity] - Error:', error);
res.status(500).json({ error: 'Error updating predefined activity' });
}
};

View File

@@ -0,0 +1,52 @@
import { DataTypes } from 'sequelize';
import sequelize from '../database.js';
import DiaryDate from './DiaryDates.js';
import PredefinedActivity from './PredefinedActivity.js';
const DiaryDateActivity = sequelize.define('DiaryDateActivity', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
},
diaryDateId: {
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: DiaryDate,
key: 'id',
},
onDelete: 'CASCADE',
},
predefinedActivityId: {
type: DataTypes.INTEGER,
allowNull: true,
references: {
model: PredefinedActivity,
key: 'id',
},
onDelete: 'SET NULL',
},
customActivityName: {
type: DataTypes.STRING,
allowNull: true,
},
duration: {
type: DataTypes.INTEGER,
allowNull: true,
},
durationText: {
type: DataTypes.STRING,
allowNull: true,
},
orderId: {
type: DataTypes.INTEGER,
allowNull: false,
},
}, {
tableName: 'diary_date_activities',
underscored: true,
timestamps: true,
});
export default DiaryDateActivity;

View File

@@ -30,5 +30,4 @@ const DiaryDate = sequelize.define('DiaryDate', {
timestamps: true
});
export default DiaryDate;

View File

@@ -2,6 +2,7 @@ import { DataTypes } from 'sequelize';
import sequelize from '../database.js';
import Member from './Member.js';
import DiaryDate from './DiaryDates.js';
import { encryptData, decryptData } from '../utils/encrypt.js';
const DiaryMemberNote = sequelize.define('DiaryMemberNote', {
memberId: {
@@ -25,6 +26,14 @@ const DiaryMemberNote = sequelize.define('DiaryMemberNote', {
content: {
type: DataTypes.STRING(4096),
allowNull: false,
set(value) {
const encryptedValue = encryptData(value);
this.setDataValue('content', encryptedValue);
},
get() {
const encryptedValue = this.getDataValue('content');
return decryptData(encryptedValue);
}
},
}, {
underscored: true,

View File

@@ -2,6 +2,7 @@ import { DataTypes } from 'sequelize';
import sequelize from '../database.js';
import Member from './Member.js';
import DiaryDate from './DiaryDates.js';
import { encryptData, decryptData } from '../utils/encrypt.js';
const Participant = sequelize.define('Participant', {
id: {
@@ -29,6 +30,18 @@ const Participant = sequelize.define('Participant', {
notes: {
type: DataTypes.STRING(4096),
allowNull: true,
set(value) {
const encryptedValue = encryptData(value);
this.setDataValue('notes', encryptedValue);
},
get() {
try {
const encryptedValue = this.getDataValue('notes');
return encryptData ? decryptData(encryptedValue) : null;
} catch (error) {
return null;
}
}
}
}, {
underscored: true,

View File

@@ -0,0 +1,32 @@
import { DataTypes } from 'sequelize';
import sequelize from '../database.js';
const PredefinedActivity = sequelize.define('PredefinedActivity', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
},
name: {
type: DataTypes.STRING,
allowNull: false,
},
description: {
type: DataTypes.TEXT,
allowNull: true,
},
durationText: {
type: DataTypes.STRING,
allowNull: true,
},
duration: {
type: DataTypes.INTEGER,
allowNull: true,
},
}, {
tableName: 'predefined_activities',
timestamps: true,
underscored: true,
});
export default PredefinedActivity;

View File

@@ -12,6 +12,8 @@ import MemberNote from './MemberNote.js';
import DiaryDateTag from './DiaryDateTag.js';
import DiaryMemberNote from './DiaryMemberNote.js';
import DiaryMemberTag from './DiaryMemberTag.js';
import PredefinedActivity from './PredefinedActivity.js';
import DiaryDateActivity from './DiaryDateActivity.js';
User.hasMany(Log, { foreignKey: 'userId' });
Log.belongsTo(User, { foreignKey: 'userId' });
@@ -45,10 +47,16 @@ DiaryTag.belongsToMany(DiaryDate, { through: DiaryDateTag, as: 'diaryDates', for
DiaryDate.belongsToMany(Member, { through: DiaryMemberNote, as: 'noteMembers', foreignKey: 'diaryDateId' });
Member.belongsToMany(DiaryDate, { through: DiaryMemberNote, as: 'noteDates', foreignKey: 'memberId' });
DiaryTag.hasMany(DiaryMemberTag, { foreignKey: 'tagId', as: 'diaryMemberTags' });
DiaryMemberTag.belongsTo(DiaryTag, { foreignKey: 'tagId', as: 'tag' });
DiaryDate.hasMany(DiaryDateActivity, { foreignKey: 'diaryDateId', as: 'diaryDateActivities' });
DiaryDateActivity.belongsTo(DiaryDate, { foreignKey: 'diaryDateId', as: 'diaryDate' });
PredefinedActivity.hasMany(DiaryDateActivity, { foreignKey: 'predefinedActivityId', as: 'predefinedActivities' });
DiaryDateActivity.belongsTo(PredefinedActivity, { foreignKey: 'predefinedActivityId', as: 'predefinedActivity' });
export {
User,
Log,
@@ -65,4 +73,6 @@ export {
DiaryDateTag,
DiaryMemberNote,
DiaryMemberTag,
PredefinedActivity,
DiaryDateActivity,
};

View File

@@ -0,0 +1,19 @@
import express from 'express';
import {
createDiaryDateActivity,
updateDiaryDateActivity,
deleteDiaryDateActivity,
updateDiaryDateActivityOrder,
getDiaryDateActivities,
} from '../controllers/diaryDateActivityController.js';
import { authenticate } from '../middleware/authMiddleware.js';
const router = express.Router();
router.post('/:clubId/', authenticate, createDiaryDateActivity);
router.put('/:clubId/:id/order', authenticate, updateDiaryDateActivityOrder);
router.put('/:clubId/:id', authenticate, updateDiaryDateActivity);
router.delete('/:clubId/:id', authenticate, deleteDiaryDateActivity);
router.get('/:clubId/:diaryDateId', authenticate, getDiaryDateActivities);
export default router;

View File

@@ -9,7 +9,7 @@ router.get('/:clubId/tag', authenticate, getMemberTags);
router.get('/:clubId/note', authenticate, getMemberNotes);
router.post('/:clubId/note', authenticate, addMemberNote);
router.post('/:clubId/tag', authenticate, addMemberTag);
router.post('/:clubId/note/remove', authenticate, removeMemberNote);
router.delete('/:clubId/note/:noteId', authenticate, removeMemberNote);
router.post('/:clubId/tag/remove', authenticate, removeMemberTag);
export default router;

View File

@@ -0,0 +1,16 @@
import express from 'express';
import {
createPredefinedActivity,
getAllPredefinedActivities,
getPredefinedActivityById,
updatePredefinedActivity,
} from '../controllers/predefinedActivityController.js';
const router = express.Router();
router.post('/', createPredefinedActivity);
router.get('/', getAllPredefinedActivities);
router.get('/:id', getPredefinedActivityById);
router.put('/:id', updatePredefinedActivity);
export default router;

View File

@@ -15,10 +15,14 @@ import activityRoutes from './routes/activityRoutes.js';
import memberNoteRoutes from './routes/memberNoteRoutes.js';
import diaryTagRoutes from './routes/diaryTagRoutes.js';
import diaryNoteRoutes from './routes/diaryNoteRoutes.js';
import diaryMemberRoutes from './routes/diaryMemberRoutes.js'; // Neue Route
import diaryMemberRoutes from './routes/diaryMemberRoutes.js';
import predefinedActivityRoutes from './routes/predefinedActivityRoutes.js';
import PredefinedActivity from './models/PredefinedActivity.js';
import DiaryDateActivity from './models/DiaryDateActivity.js';
import diaryDateActivityRoutes from './routes/diaryDateActivityRoutes.js';
const app = express();
const port = process.env.PORT || 3000;
const port = process.env.PORT || 3000;
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
@@ -35,6 +39,8 @@ app.use('/api/membernotes', memberNoteRoutes);
app.use('/api/diarynotes', diaryNoteRoutes);
app.use('/api/tags', diaryTagRoutes);
app.use('/api/diarymember', diaryMemberRoutes); // Neue Route für Diary-Member-Funktionalität
app.use('/api/predefined-activities', predefinedActivityRoutes);
app.use('/api/diary-date-activities', diaryDateActivityRoutes);
app.use(express.static(path.join(__dirname, '../frontend/dist')));
@@ -61,6 +67,8 @@ app.get('*', (req, res) => {
await DiaryDateTag.sync({ alter: true });
await DiaryMemberTag.sync({ alter: true });
await DiaryMemberNote.sync({ alter: true });
await PredefinedActivity.sync({ alter: true });
await DiaryDateActivity.sync({ alter: true });
app.listen(port, () => {
console.log(`Server is running on http://localhost:${port}`);

View File

@@ -0,0 +1,130 @@
import DiaryDateActivity from '../models/DiaryDateActivity.js';
import PredefinedActivity from '../models/PredefinedActivity.js';
import { checkAccess } from '../utils/userUtils.js';
import { Op } from 'sequelize';
class DiaryDateActivityService {
async createActivity(userToken, clubId, data) {
console.log('[DiaryDateActivityService::createActivity] - check user access');
await checkAccess(userToken, clubId);
console.log('[DiaryDateActivityService::createActivity] - add: ', data);
let predefinedActivity = await PredefinedActivity.findOne({ where: { name: data.activity } });
if (!predefinedActivity) {
predefinedActivity = await PredefinedActivity.create({
name: data.activity,
description: '',
duration: data.duration
});
}
const maxOrderId = await DiaryDateActivity.max('orderId', {
where: { diaryDateId: data.diaryDateId }
});
const newOrderId = maxOrderId !== null ? maxOrderId + 1 : 1;
const { activity, ...restData } = data;
restData.predefinedActivityId = predefinedActivity.id;
restData.orderId = newOrderId;
console.log('[DiaryDateActivityService::createActivity] - create diary date activity');
return await DiaryDateActivity.create(restData);
}
async updateActivity(userToken, clubId, id, data) {
console.log('[DiaryDateActivityService::upateActivity] - check user access');
await checkAccess(userToken, clubId);
console.log('[DiaryDateActivityService::updateActivity] - load activit', id);
const activity = await DiaryDateActivity.findByPk(id);
if (!activity) {
console.log('[DiaryDateActivityService::updateActivity] - activity not found');
throw new Error('Activity not found');
}
console.log('[DiaryDateActivityService::updateActivity] - update activity');
return await activity.update(data);
}
async deleteActivity(userToken, clubId, id) {
await checkAccess(userToken, clubId);
const activity = await DiaryDateActivity.findByPk(id);
if (!activity) {
throw new Error('Activity not found');
}
return await activity.destroy();
}
async updateActivityOrder(userToken, clubId, id, newOrderId) {
console.log(`[DiaryDateActivityService::updateActivityOrder] - Start update for activity id: ${id}`);
console.log(`[DiaryDateActivityService::updateActivityOrder] - User token: ${userToken}, Club id: ${clubId}, New order id: ${newOrderId}`);
console.log('[DiaryDateActivityService::updateActivityOrder] - Checking user access');
await checkAccess(userToken, clubId);
console.log('[DiaryDateActivityService::updateActivityOrder] - User access confirmed');
console.log(`[DiaryDateActivityService::updateActivityOrder] - Finding activity with id: ${id}`);
const activity = await DiaryDateActivity.findByPk(id);
if (!activity) {
console.error('[DiaryDateActivityService::updateActivityOrder] - Activity not found, throwing error');
throw new Error('Activity not found');
}
console.log('[DiaryDateActivityService::updateActivityOrder] - Activity found:', activity);
const currentOrderId = activity.orderId;
console.log(`[DiaryDateActivityService::updateActivityOrder] - Current order id: ${currentOrderId}`);
if (newOrderId < currentOrderId) {
console.log(`[DiaryDateActivityService::updateActivityOrder] - Shifting items down. Moving activities with orderId between ${newOrderId} and ${currentOrderId - 1}`);
await DiaryDateActivity.increment(
{ orderId: 1 },
{
where: {
diaryDateId: activity.diaryDateId,
orderId: { [Op.gte]: newOrderId, [Op.lt]: currentOrderId },
},
}
);
console.log(`[DiaryDateActivityService::updateActivityOrder] - Items shifted down`);
} else if (newOrderId > currentOrderId) {
console.log(`[DiaryDateActivityService::updateActivityOrder] - Shifting items up. Moving activities with orderId between ${currentOrderId + 1} and ${newOrderId}`);
await DiaryDateActivity.decrement(
{ orderId: 1 },
{
where: {
diaryDateId: activity.diaryDateId,
orderId: { [Op.lte]: newOrderId, [Op.gt]: currentOrderId },
},
}
);
console.log(`[DiaryDateActivityService::updateActivityOrder] - Items shifted up`);
} else {
console.log('[DiaryDateActivityService::updateActivityOrder] - New order id is the same as the current order id. No shift required.');
}
console.log(`[DiaryDateActivityService::updateActivityOrder] - Setting new order id for activity id: ${id}`);
activity.orderId = newOrderId;
console.log('[DiaryDateActivityService::updateActivityOrder] - Saving activity with new order id');
const savedActivity = await activity.save();
console.log('[DiaryDateActivityService::updateActivityOrder] - Activity saved:', savedActivity);
console.log(`[DiaryDateActivityService::updateActivityOrder] - Finished update for activity id: ${id}`);
return savedActivity;
}
async getActivities(userToken, clubId, diaryDateId) {
console.log('[DiaryDateActivityService::getActivities] - check user access');
await checkAccess(userToken, clubId);
console.log(`[DiaryDateActivityService::getActivities] - fetch activities for diaryDateId: ${diaryDateId}`);
const activities = await DiaryDateActivity.findAll({
where: { diaryDateId },
order: [['orderId', 'ASC']],
include: [
{
model: PredefinedActivity,
as: 'predefinedActivity', // Assuming 'predefinedActivity' is the alias used in the model association
}
]
});
console.log(`[DiaryDateActivityService::getActivities] - found ${activities.length} activities`);
return activities;
}
}
export default new DiaryDateActivityService();

View File

@@ -33,10 +33,9 @@ class DiaryMemberService {
});
}
async removeNoteFromMember(userToken, clubId, diaryDateId, memberId, content) {
async removeNoteFromMember(userToken, clubId, noteId) {
await checkAccess(userToken, clubId);
const note = await DiaryMemberNote.findOne({ where: { diaryDateId, memberId, content } });
const note = await DiaryMemberNote.findOne({ where: { id: noteId } });
if (note) {
await note.destroy();
} else {

View File

@@ -0,0 +1,45 @@
import PredefinedActivity from '../models/PredefinedActivity.js';
class PredefinedActivityService {
async createPredefinedActivity(data) {
console.log('[PredefinedActivityService::createPredefinedActivity] - Creating predefined activity');
return await PredefinedActivity.create({
name: data.name,
description: data.description,
durationText: data.durationText,
duration: data.duration,
});
}
async updatePredefinedActivity(id, data) {
console.log(`[PredefinedActivityService::updatePredefinedActivity] - Updating predefined activity with id: ${id}`);
const activity = await PredefinedActivity.findByPk(id);
if (!activity) {
console.log('[PredefinedActivityService::updatePredefinedActivity] - Activity not found');
throw new Error('Predefined activity not found');
}
return await activity.update({
name: data.name,
description: data.description,
durationText: data.durationText,
duration: data.duration,
});
}
async getAllPredefinedActivities() {
console.log('[PredefinedActivityService::getAllPredefinedActivities] - Fetching all predefined activities');
return await PredefinedActivity.findAll();
}
async getPredefinedActivityById(id) {
console.log(`[PredefinedActivityService::getPredefinedActivityById] - Fetching predefined activity with id: ${id}`);
const activity = await PredefinedActivity.findByPk(id);
if (!activity) {
console.log('[PredefinedActivityService::getPredefinedActivityById] - Activity not found');
throw new Error('Predefined activity not found');
}
return activity;
}
}
export default new PredefinedActivityService();