Added groups to diary
This commit is contained in:
@@ -4,13 +4,14 @@ 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 { diaryDateId, activity, duration, durationText, orderId, isTimeblock } = req.body;
|
||||
const activityItem = await diaryDateActivityService.createActivity(userToken, clubId, {
|
||||
diaryDateId,
|
||||
activity,
|
||||
duration,
|
||||
durationText,
|
||||
orderId,
|
||||
isTimeblock,
|
||||
});
|
||||
res.status(201).json(activityItem);
|
||||
} catch (error) {
|
||||
@@ -23,13 +24,14 @@ 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 { predefinedActivityId, customActivityName, duration, durationText, orderId, groupId } = req.body; // Add groupId
|
||||
const updatedActivity = await diaryDateActivityService.updateActivity(userToken, clubId, id, {
|
||||
predefinedActivityId,
|
||||
customActivityName,
|
||||
duration,
|
||||
durationText,
|
||||
orderId,
|
||||
groupId, // Pass groupId to the service
|
||||
});
|
||||
res.status(200).json(updatedActivity);
|
||||
} catch (error) {
|
||||
@@ -68,6 +70,19 @@ export const getDiaryDateActivities = async (req, res) => {
|
||||
const activities = await diaryDateActivityService.getActivities(userToken, clubId, diaryDateId);
|
||||
res.status(200).json(activities);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
res.status(500).json({ error: 'Error getting activities' });
|
||||
}
|
||||
}
|
||||
|
||||
export const addGroupActivity = async(req, res) => {
|
||||
try {
|
||||
const { authcode: userToken } = req.headers;
|
||||
const { clubId, diaryDateId, groupId, activity } = req.body;
|
||||
const activityItem = await diaryDateActivityService.addGroupActivity(userToken, clubId, diaryDateId, groupId, activity);
|
||||
res.status(201).json(activityItem);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
res.status(500).json({ error: 'Error adding group activity' });
|
||||
}
|
||||
}
|
||||
29
backend/controllers/groupController.js
Normal file
29
backend/controllers/groupController.js
Normal file
@@ -0,0 +1,29 @@
|
||||
import HttpError from '../exceptions/HttpError.js';
|
||||
import groupService from '../services/groupService.js';
|
||||
|
||||
const addGroup = async(req, res) => {
|
||||
try {
|
||||
const { authcode: userToken } = req.headers;
|
||||
const { clubid: clubId, dateid: dateId, name, lead } = req.body;
|
||||
const result = await groupService.addGroup(userToken, clubId, dateId, name, lead);
|
||||
res.status(201).json(result);
|
||||
} catch (error) {
|
||||
console.error('[addGroup] - Error:', error);
|
||||
console.log(req.params, req.headers, req.body)
|
||||
res.status(error.statusCode || 500).json({ error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
const getGroups = async(req, res) => {
|
||||
try {
|
||||
const { clubId, dateId } = req.params;
|
||||
const { authcode: userToken } = req.headers;
|
||||
const result = await groupService.getGroups(userToken, clubId, dateId);
|
||||
res.status(200).json(result);
|
||||
} catch (error) {
|
||||
console.error('[getGroups] - Error:', error);
|
||||
res.status(error.statusCode || 500).json({ error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
export { addGroup, getGroups };
|
||||
@@ -11,13 +11,18 @@ const DiaryDateActivity = sequelize.define('DiaryDateActivity', {
|
||||
},
|
||||
diaryDateId: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
allowNull: true,
|
||||
references: {
|
||||
model: DiaryDate,
|
||||
key: 'id',
|
||||
},
|
||||
onDelete: 'CASCADE',
|
||||
},
|
||||
isTimeblock: {
|
||||
type: DataTypes.BOOLEAN,
|
||||
allowNull: false,
|
||||
defaultValue: false,
|
||||
},
|
||||
predefinedActivityId: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
@@ -27,11 +32,7 @@ const DiaryDateActivity = sequelize.define('DiaryDateActivity', {
|
||||
},
|
||||
onDelete: 'SET NULL',
|
||||
},
|
||||
customActivityName: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
},
|
||||
duration: {
|
||||
duration: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
},
|
||||
|
||||
@@ -3,7 +3,13 @@ import sequelize from '../database.js';
|
||||
import DiaryDate from './DiaryDates.js';
|
||||
|
||||
const Group = sequelize.define('Group', {
|
||||
diaryDate: {
|
||||
id: {
|
||||
type: DataTypes.INTEGER,
|
||||
primaryKey: true,
|
||||
autoIncrement: true,
|
||||
allowNull: false,
|
||||
},
|
||||
diaryDateId: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
references: {
|
||||
|
||||
40
backend/models/GroupActivity.js
Normal file
40
backend/models/GroupActivity.js
Normal file
@@ -0,0 +1,40 @@
|
||||
import { DataTypes } from 'sequelize';
|
||||
import sequelize from '../database.js';
|
||||
import DiaryDateActivity from './DiaryDateActivity.js';
|
||||
import Group from './Group.js';
|
||||
|
||||
const GroupActivity = sequelize.define('GroupActivity', {
|
||||
id: {
|
||||
type: DataTypes.INTEGER,
|
||||
primaryKey: true,
|
||||
autoIncrement: true,
|
||||
},
|
||||
diaryDateActivity: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
references: {
|
||||
model: DiaryDateActivity,
|
||||
key: 'id'
|
||||
},
|
||||
onDelete: 'CASCADE'
|
||||
},
|
||||
groupId: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
references: {
|
||||
model: Group,
|
||||
key: 'id',
|
||||
},
|
||||
onDelete: 'CASCADE'
|
||||
},
|
||||
customActivity: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
},
|
||||
}, {
|
||||
tableName: 'group_activity',
|
||||
underscored: true,
|
||||
timestamps: true,
|
||||
});
|
||||
|
||||
export default GroupActivity;
|
||||
@@ -19,6 +19,8 @@ import League from './League.js';
|
||||
import Team from './Team.js';
|
||||
import Season from './Season.js';
|
||||
import Location from './Location.js';
|
||||
import Group from './Group.js';
|
||||
import GroupActivity from './GroupActivity.js';
|
||||
|
||||
User.hasMany(Log, { foreignKey: 'userId' });
|
||||
Log.belongsTo(User, { foreignKey: 'userId' });
|
||||
@@ -94,6 +96,17 @@ User.hasMany(UserClub, { foreignKey: 'userId' });
|
||||
UserClub.belongsTo(Club, { foreignKey: 'clubId', as: 'club' });
|
||||
Club.hasMany(UserClub, { foreignKey: 'clubId' });
|
||||
|
||||
Group.belongsTo(DiaryDate, { foreignKey: 'diaryDateId', as: 'diaryDateGroup' });
|
||||
DiaryDate.hasMany(Group, { foreignKey: 'diaryDateId', as: 'groupsDiaryDate' });
|
||||
|
||||
GroupActivity.belongsTo(DiaryDateActivity, { foreignKey: 'id', as: 'activityGroupActivity' });
|
||||
DiaryDateActivity.hasMany(GroupActivity, { foreignKey: 'diaryDateActivity', as: 'groupActivities' });
|
||||
|
||||
Group.hasOne(GroupActivity, { foreignKey: 'groupId', as: 'groupGroupActivity' });
|
||||
GroupActivity.belongsTo(Group, { foreignKey: 'groupId', as: 'groupsGroupActivity' });
|
||||
|
||||
GroupActivity.belongsTo(PredefinedActivity, { foreignKey: 'customActivity', as: 'groupPredefinedActivity' });
|
||||
PredefinedActivity.hasMany(GroupActivity, { foreignKey: 'predefinedActivityId', as: 'groupPredefinedActivities' });
|
||||
|
||||
export {
|
||||
User,
|
||||
@@ -116,4 +129,6 @@ export {
|
||||
Match,
|
||||
League,
|
||||
Team,
|
||||
Group,
|
||||
GroupActivity,
|
||||
};
|
||||
|
||||
@@ -5,11 +5,13 @@ import {
|
||||
deleteDiaryDateActivity,
|
||||
updateDiaryDateActivityOrder,
|
||||
getDiaryDateActivities,
|
||||
addGroupActivity,
|
||||
} from '../controllers/diaryDateActivityController.js';
|
||||
import { authenticate } from '../middleware/authMiddleware.js';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.post('/group', authenticate, addGroupActivity);
|
||||
router.post('/:clubId/', authenticate, createDiaryDateActivity);
|
||||
router.put('/:clubId/:id/order', authenticate, updateDiaryDateActivityOrder);
|
||||
router.put('/:clubId/:id', authenticate, updateDiaryDateActivity);
|
||||
|
||||
10
backend/routes/groupRoutes.js
Normal file
10
backend/routes/groupRoutes.js
Normal file
@@ -0,0 +1,10 @@
|
||||
import express from 'express';
|
||||
import { authenticate } from '../middleware/authMiddleware.js';
|
||||
import { addGroup, getGroups } from '../controllers/groupController.js';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.post('/', authenticate, addGroup);
|
||||
router.get('/:clubId/:dateId', authenticate, getGroups);
|
||||
|
||||
export default router;
|
||||
@@ -5,7 +5,8 @@ import sequelize from './database.js';
|
||||
import {
|
||||
User, Log, Club, UserClub, Member, DiaryDate, Participant, Activity, MemberNote,
|
||||
DiaryNote, DiaryTag, MemberDiaryTag, DiaryDateTag, DiaryMemberNote, DiaryMemberTag,
|
||||
PredefinedActivity, DiaryDateActivity, Match, League, Team // Import der neuen Modelle
|
||||
PredefinedActivity, DiaryDateActivity, Match, League, Team, Group,
|
||||
GroupActivity
|
||||
} from './models/index.js';
|
||||
import authRoutes from './routes/authRoutes.js';
|
||||
import clubRoutes from './routes/clubRoutes.js';
|
||||
@@ -22,6 +23,7 @@ import diaryDateActivityRoutes from './routes/diaryDateActivityRoutes.js';
|
||||
import matchRoutes from './routes/matchRoutes.js';
|
||||
import Season from './models/Season.js';
|
||||
import Location from './models/Location.js';
|
||||
import groupRoutes from './routes/groupRoutes.js';
|
||||
|
||||
const app = express();
|
||||
const port = process.env.PORT || 3000;
|
||||
@@ -44,6 +46,7 @@ app.use('/api/diarymember', diaryMemberRoutes);
|
||||
app.use('/api/predefined-activities', predefinedActivityRoutes);
|
||||
app.use('/api/diary-date-activities', diaryDateActivityRoutes);
|
||||
app.use('/api/matches', matchRoutes);
|
||||
app.use('/api/group', groupRoutes);
|
||||
|
||||
app.use(express.static(path.join(__dirname, '../frontend/dist')));
|
||||
|
||||
@@ -76,7 +79,9 @@ app.get('*', (req, res) => {
|
||||
await League.sync({ alter: true });
|
||||
await Team.sync({ alter: true });
|
||||
await Location.sync({ alter: true });
|
||||
await Match.sync({ alter: true }); // Match zuletzt, um sicherzustellen, dass alle Referenzen existieren
|
||||
await Match.sync({ alter: true });
|
||||
await Group.sync({ alter: true });
|
||||
await GroupActivity.sync({ alter: true });
|
||||
|
||||
app.listen(port, () => {
|
||||
console.log(`Server is running on http://localhost:${port}`);
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import DiaryDateActivity from '../models/DiaryDateActivity.js';
|
||||
import GroupActivity from '../models/GroupActivity.js';
|
||||
import Group from '../models/Group.js';
|
||||
import PredefinedActivity from '../models/PredefinedActivity.js';
|
||||
import { checkAccess } from '../utils/userUtils.js';
|
||||
import { Op } from 'sequelize';
|
||||
@@ -9,6 +11,7 @@ class DiaryDateActivityService {
|
||||
console.log('[DiaryDateActivityService::createActivity] - check user access');
|
||||
await checkAccess(userToken, clubId);
|
||||
console.log('[DiaryDateActivityService::createActivity] - add: ', data);
|
||||
const { activity, ...restData } = data;
|
||||
let predefinedActivity = await PredefinedActivity.findOne({ where: { name: data.activity } });
|
||||
if (!predefinedActivity) {
|
||||
predefinedActivity = await PredefinedActivity.create({
|
||||
@@ -17,21 +20,20 @@ class DiaryDateActivityService {
|
||||
duration: data.duration
|
||||
});
|
||||
}
|
||||
restData.predefinedActivityId = predefinedActivity.id;
|
||||
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');
|
||||
console.log('[DiaryDateActivityService::updateActivity] - check user access');
|
||||
await checkAccess(userToken, clubId);
|
||||
console.log('[DiaryDateActivityService::updateActivity] - load activit', id);
|
||||
console.log('[DiaryDateActivityService::updateActivity] - load activity', id);
|
||||
const activity = await DiaryDateActivity.findByPk(id);
|
||||
if (!activity) {
|
||||
console.log('[DiaryDateActivityService::updateActivity] - activity not found');
|
||||
@@ -53,11 +55,9 @@ class DiaryDateActivityService {
|
||||
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) {
|
||||
@@ -65,10 +65,8 @@ class DiaryDateActivityService {
|
||||
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(
|
||||
@@ -96,14 +94,11 @@ class DiaryDateActivityService {
|
||||
} 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;
|
||||
}
|
||||
@@ -118,13 +113,66 @@ class DiaryDateActivityService {
|
||||
include: [
|
||||
{
|
||||
model: PredefinedActivity,
|
||||
as: 'predefinedActivity',
|
||||
as: 'predefinedActivity',
|
||||
},
|
||||
{
|
||||
model: GroupActivity,
|
||||
as: 'groupActivities',
|
||||
include: [
|
||||
{
|
||||
model: Group,
|
||||
as: 'groupsGroupActivity'
|
||||
},
|
||||
{
|
||||
model: PredefinedActivity,
|
||||
as: 'groupPredefinedActivity',
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
});
|
||||
console.log(`[DiaryDateActivityService::getActivities] - found ${activities.length} activities`);
|
||||
return activities;
|
||||
}
|
||||
|
||||
async addGroupActivity(userToken, clubId, diaryDateId, groupId, activity) {
|
||||
console.log('[DiaryDateActivityService::addGroupActivity] Check user access');
|
||||
await checkAccess(userToken, clubId);
|
||||
console.log('[DiaryDateActivityService::addGroupActivity] Check diary date');
|
||||
const diaryDateActivity = await DiaryDateActivity.findOne({
|
||||
where: {
|
||||
diaryDateId,
|
||||
isTimeblock: true,
|
||||
},
|
||||
order: [['order_id', 'ASC']],
|
||||
limit: 1
|
||||
});
|
||||
if (!diaryDateActivity) {
|
||||
console.error('[DiaryDateActivityService::addGroupActivity] Activity not found');
|
||||
throw new Error('Activity not found');
|
||||
}
|
||||
console.log('[DiaryDateActivityService::addGroupActivity] Check group');
|
||||
const group = await Group.findByPk(groupId);
|
||||
if (!group || group.diaryDateId !== diaryDateActivity.diaryDateId) {
|
||||
console.error('[DiaryDateActivityService::addGroupActivity] Group and date don\'t fit');
|
||||
throw new Error('Group isn\'t related to date');
|
||||
}
|
||||
console.log('[DiaryDateActivityService::addGroupActivity] Get predefined activity');
|
||||
const [predefinedActivity, created] = await PredefinedActivity.findOrCreate({
|
||||
where: {
|
||||
name: activity
|
||||
}
|
||||
});
|
||||
console.log('[DiaryDateActivityService::addGroupActivity] Add group activity');
|
||||
console.log(predefinedActivity);
|
||||
const activityData = {
|
||||
diaryDateActivity: diaryDateActivity.id,
|
||||
groupId: groupId,
|
||||
customActivity: predefinedActivity.id
|
||||
}
|
||||
console.log(activityData);
|
||||
return await GroupActivity.create(activityData);
|
||||
}
|
||||
}
|
||||
|
||||
export default new DiaryDateActivityService();
|
||||
|
||||
43
backend/services/groupService.js
Normal file
43
backend/services/groupService.js
Normal file
@@ -0,0 +1,43 @@
|
||||
import { checkAccess } from '../utils/userUtils.js';
|
||||
import DiaryDate from '../models/DiaryDates.js';
|
||||
import HttpError from '../exceptions/HttpError.js';
|
||||
import Group from '../models/Group.js';
|
||||
|
||||
class GroupService {
|
||||
|
||||
async checkDiaryDateToClub(clubId, dateId) {
|
||||
const diaryDate = await DiaryDate.findOne({
|
||||
where: {
|
||||
id: dateId,
|
||||
clubId: clubId
|
||||
}
|
||||
});
|
||||
if (!diaryDate) {
|
||||
throw new HttpError('Datum nicht gefunden oder passt nicht zum Verein', 404);
|
||||
}
|
||||
}
|
||||
|
||||
async addGroup(userToken, clubId, dateId, name, lead) {
|
||||
await checkAccess(userToken, clubId);
|
||||
await this.checkDiaryDateToClub(clubId, dateId);
|
||||
const group = await Group.create({
|
||||
diaryDateId: dateId,
|
||||
name,
|
||||
lead
|
||||
});
|
||||
return group;
|
||||
}
|
||||
|
||||
async getGroups(userToken, clubId, dateId) {
|
||||
await checkAccess(userToken, clubId);
|
||||
await this.checkDiaryDateToClub(clubId, dateId);
|
||||
const groups = await Group.findAll({
|
||||
where: {
|
||||
diaryDateId: dateId
|
||||
}
|
||||
});
|
||||
return groups;
|
||||
}
|
||||
}
|
||||
|
||||
export default new GroupService();
|
||||
@@ -120,7 +120,7 @@ class MatchService {
|
||||
const leagues = await League.findAll({
|
||||
include: [{
|
||||
model: Match,
|
||||
as: 'leagueMatches', // Verwendung des Alias 'leagueMatches'
|
||||
as: 'leagueMatches',
|
||||
where: {
|
||||
seasonId: season.id,
|
||||
clubId: clubId
|
||||
|
||||
@@ -6,12 +6,17 @@ class PDFGenerator {
|
||||
this.pdf = new jsPDF('p', 'mm', 'a4');
|
||||
this.margin = margin;
|
||||
this.columnGap = columnGap;
|
||||
this.pageHeight = 295 - margin * 2;
|
||||
this.pageHeight = 297 - margin * 2;
|
||||
this.columnWidth = (210 - margin * 2 - columnGap) / 2;
|
||||
this.position = margin;
|
||||
this.yPos = this.position;
|
||||
this.xPos = margin;
|
||||
this.isLeftColumn = true;
|
||||
this.COLUMN_START_TIME = margin;
|
||||
this.COLUMN_ACTIVITY = margin + 30;
|
||||
this.COLUMN_GROUP = margin + 100;
|
||||
this.COLUMN_DURATION = margin + 150;
|
||||
this.LINE_HEIGHT = 7;
|
||||
}
|
||||
|
||||
async addSchedule(element) {
|
||||
@@ -38,52 +43,7 @@ class PDFGenerator {
|
||||
this.isLeftColumn = true;
|
||||
}
|
||||
|
||||
addHeader(title) {
|
||||
this.pdf.setFontSize(12);
|
||||
this.pdf.setFont('helvetica', 'bold');
|
||||
this.pdf.text(title, this.margin, this.position);
|
||||
this.pdf.setLineWidth(0.5);
|
||||
this.pdf.line(this.margin, this.position + 2, 210 - this.margin, this.position + 2);
|
||||
this.yPos += 10;
|
||||
this.position = this.yPos;
|
||||
}
|
||||
|
||||
addAddress(clubName, addressLines) {
|
||||
this.pdf.setFontSize(10);
|
||||
this.pdf.setFont('helvetica', 'bold');
|
||||
this.pdf.text(clubName, this.xPos, this.yPos);
|
||||
this.yPos += 5;
|
||||
|
||||
this.pdf.setFont('helvetica', 'normal');
|
||||
addressLines.forEach(line => {
|
||||
this.pdf.text(line, this.xPos, this.yPos);
|
||||
this.yPos += 5;
|
||||
});
|
||||
this.yPos += 10;
|
||||
this.checkColumnOverflow();
|
||||
}
|
||||
|
||||
checkColumnOverflow() {
|
||||
if (this.isLeftColumn) {
|
||||
if (this.yPos > this.pageHeight) {
|
||||
this.xPos += this.columnWidth + this.columnGap;
|
||||
this.yPos = this.position;
|
||||
this.isLeftColumn = false;
|
||||
}
|
||||
} else {
|
||||
if (this.yPos > this.pageHeight) {
|
||||
this.pdf.addPage();
|
||||
this.xPos = this.margin;
|
||||
this.yPos = this.position;
|
||||
this.isLeftColumn = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addTrainingPlan(clubName, trainingDate, trainingStart, trainingEnd, trainingPlan) {
|
||||
const formattedDate = new Date(trainingDate).toLocaleDateString('de-DE');
|
||||
const formattedStartTime = trainingStart.slice(0, 5);
|
||||
const formattedEndTime = trainingEnd.slice(0, 5);
|
||||
addHeader(clubName, formattedDate, formattedStartTime, formattedEndTime) {
|
||||
this.pdf.setFontSize(14);
|
||||
this.pdf.setFont('helvetica', 'bold');
|
||||
this.pdf.text(`${clubName} - Trainingsplan`, this.margin, this.yPos);
|
||||
@@ -94,42 +54,88 @@ class PDFGenerator {
|
||||
this.yPos += 7;
|
||||
this.pdf.text(`Uhrzeit: ${formattedStartTime} - ${formattedEndTime}`, this.margin, this.yPos);
|
||||
this.yPos += 10;
|
||||
}
|
||||
|
||||
addTableHeaders() {
|
||||
this.pdf.setFont('helvetica', 'bold');
|
||||
this.pdf.text('Uhrzeit', this.margin, this.yPos);
|
||||
this.pdf.text('Aktivität', this.margin + 60, this.yPos);
|
||||
this.pdf.text('Länge / Gesamtzeit (Min)', this.margin + 150, this.yPos);
|
||||
this.yPos += 10;
|
||||
this.pdf.text('Startzeit', this.COLUMN_START_TIME, this.yPos);
|
||||
this.pdf.text('Aktivität / Zeitblock', this.COLUMN_ACTIVITY, this.yPos);
|
||||
this.pdf.text('Gruppe', this.COLUMN_GROUP, this.yPos);
|
||||
this.pdf.text('Dauer (Min)', this.COLUMN_DURATION, this.yPos);
|
||||
this.yPos += this.LINE_HEIGHT;
|
||||
this.pdf.setFont('helvetica', 'normal');
|
||||
trainingPlan.forEach((item, index) => {
|
||||
const time = this.calculatePlanItemTime(index, trainingStart, trainingPlan);
|
||||
this.pdf.text(time.slice(0, 5), this.margin, this.yPos);
|
||||
this.pdf.text(item.predefinedActivity.name, this.margin + 60, this.yPos);
|
||||
this.pdf.text(item.duration.toString(), this.margin + 150, this.yPos);
|
||||
this.yPos += 7;
|
||||
}
|
||||
|
||||
if (this.yPos > this.pageHeight) {
|
||||
this.addNewPage();
|
||||
addTrainingPlan(clubName, trainingDate, trainingStart, trainingEnd, trainingPlan) {
|
||||
const formattedDate = new Date(trainingDate).toLocaleDateString('de-DE');
|
||||
const formattedStartTime = trainingStart ? trainingStart.slice(0, 5) : '';
|
||||
const formattedEndTime = trainingEnd ? trainingEnd.slice(0, 5) : '';
|
||||
this.addHeader(clubName, formattedDate, formattedStartTime, formattedEndTime);
|
||||
this.addTableHeaders();
|
||||
for (const item of trainingPlan) {
|
||||
if (item.isTimeblock) {
|
||||
this.addTimeBlock(item);
|
||||
} else {
|
||||
this.addActivity(item);
|
||||
}
|
||||
});
|
||||
this.checkPageOverflow();
|
||||
}
|
||||
}
|
||||
|
||||
calculatePlanItemTime(index, startTime, trainingPlan) {
|
||||
let time = startTime;
|
||||
for (let i = 0; i < index; i++) {
|
||||
time = this.addDurationToTime(time, trainingPlan[i].duration);
|
||||
addTimeBlock(item) {
|
||||
this.pdf.setFont('helvetica');
|
||||
this.pdf.text(item.startTime, this.COLUMN_START_TIME, this.yPos);
|
||||
this.pdf.text('Zeitblock', this.COLUMN_ACTIVITY, this.yPos);
|
||||
this.yPos += this.LINE_HEIGHT;
|
||||
this.pdf.setFont('helvetica', 'normal');
|
||||
if (item.groupActivities && item.groupActivities.length > 0) {
|
||||
this.addGroupActivities(item.groupActivities);
|
||||
}
|
||||
return time;
|
||||
}
|
||||
|
||||
addDurationToTime(startTime, duration) {
|
||||
let [hours, minutes] = startTime.split(':').map(Number);
|
||||
minutes += Number(duration);
|
||||
if (minutes >= 60) {
|
||||
hours += Math.floor(minutes / 60);
|
||||
minutes = minutes % 60;
|
||||
addActivity(item) {
|
||||
const startTime = item.startTime;
|
||||
const activityName = item.predefinedActivity
|
||||
? item.predefinedActivity.name
|
||||
: item.activity;
|
||||
const groupName = item.groupActivity ? item.groupActivity.name : '';
|
||||
const duration = item.duration ? item.duration.toString() : '';
|
||||
let text = '';
|
||||
if (item.durationText) {
|
||||
text = item.durationText + ' / ';
|
||||
}
|
||||
text += duration;
|
||||
this.pdf.text(startTime, this.COLUMN_START_TIME, this.yPos);
|
||||
this.pdf.text(activityName, this.COLUMN_ACTIVITY, this.yPos);
|
||||
this.pdf.text(groupName, this.COLUMN_GROUP, this.yPos);
|
||||
this.pdf.text(text, this.COLUMN_DURATION, this.yPos);
|
||||
this.yPos += this.LINE_HEIGHT;
|
||||
if (item.groupActivities && item.groupActivities.length > 0) {
|
||||
this.addGroupActivities(item.groupActivities);
|
||||
}
|
||||
}
|
||||
|
||||
addGroupActivities(groupActivities) {
|
||||
for (const groupItem of groupActivities) {
|
||||
const activityName = groupItem.groupPredefinedActivity
|
||||
? groupItem.groupPredefinedActivity.name
|
||||
: '';
|
||||
const groupName = groupItem.groupsGroupActivity
|
||||
? groupItem.groupsGroupActivity.name
|
||||
: '';
|
||||
this.pdf.text('', this.COLUMN_START_TIME, this.yPos); // Leere Startzeit
|
||||
this.pdf.text(' - ' + activityName, this.COLUMN_ACTIVITY + 5, this.yPos); // Einrückung
|
||||
this.pdf.text(groupName, this.COLUMN_GROUP, this.yPos);
|
||||
this.pdf.text('', this.COLUMN_DURATION, this.yPos); // Leere Dauer
|
||||
this.yPos += this.LINE_HEIGHT;
|
||||
this.checkPageOverflow();
|
||||
}
|
||||
}
|
||||
|
||||
checkPageOverflow() {
|
||||
if (this.yPos > this.pageHeight) {
|
||||
this.addNewPage();
|
||||
}
|
||||
hours = hours % 24;
|
||||
return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`;
|
||||
}
|
||||
|
||||
save(filename) {
|
||||
|
||||
@@ -5,7 +5,9 @@ import store from './store';
|
||||
import '@/assets/css/main.scss';
|
||||
import './assets/css/vue-multiselect.css';
|
||||
|
||||
createApp(App)
|
||||
const app = createApp(App);
|
||||
app.config.devtools = true;
|
||||
app
|
||||
.use(router)
|
||||
.use(store)
|
||||
.mount('#app');
|
||||
|
||||
@@ -73,11 +73,6 @@ const store = createStore({
|
||||
clubs: state => state.clubs,
|
||||
currentClubName: state => {
|
||||
const club = state.clubs.find(club => club.id === parseInt(state.currentClub));
|
||||
if (club) {
|
||||
console.log('Found Club:', club.name);
|
||||
} else {
|
||||
console.log('Club not found');
|
||||
}
|
||||
return club ? club.name : '';
|
||||
}
|
||||
},
|
||||
|
||||
@@ -46,50 +46,105 @@
|
||||
<div v-if="date !== 'new' && date !== null">
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<h3>Gruppenverwaltung</h3>
|
||||
<div>
|
||||
<h4>Vorhandene Gruppen</h4>
|
||||
<ul>
|
||||
<li v-for="group in groups" :key="group.id">
|
||||
{{ group.name }} (Leiter: {{ group.lead }})
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h4>Neue Gruppe erstellen</h4>
|
||||
<div class="groups">
|
||||
<div>
|
||||
<label for="groupName">Gruppenname:</label>
|
||||
<input type="text" v-model="newGroupName" required />
|
||||
</div>
|
||||
<div>
|
||||
<label for="groupLead">Leiter:</label>
|
||||
<input type="text" v-model="newGroupLead" required />
|
||||
</div>
|
||||
<div>
|
||||
<label> </label>
|
||||
<button type="submit" @click="addGroup">Gruppe hinzufügen</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<h3>Trainingsplan</h3>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Uhrzeit</th>
|
||||
<th>Aktivität</th>
|
||||
<th>Länge / Gesamtzeit (Min)</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody ref="sortableList">
|
||||
<tr v-for="(planItem, index) in trainingPlan" :key="planItem.id">
|
||||
<td class="drag-handle">☰</td>
|
||||
<td>{{ calculatePlanItemTime(index) }}</td>
|
||||
<td>{{ planItem.predefinedActivity.name }}</td>
|
||||
<td>{{ planItem.durationText }} / {{ planItem.duration }}</td>
|
||||
<td><span @click="removePlanItem(planItem.id)" class="add-plan-item">-</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>{{ calculateNextTime }}</td>
|
||||
<td>
|
||||
<input type="text" v-model="newPlanItem.activity" @input="handleActivityInput"
|
||||
placeholder="Aktivität eingeben" />
|
||||
<div v-if="showDropdown" class="dropdown">
|
||||
<div v-for="activity in filteredPredefinedActivities" :key="activity.id"
|
||||
@click="selectPredefinedActivity(activity)">
|
||||
{{ activity.name }} ({{ activity.durationText || '' }} / {{
|
||||
activity.duration }} Minuten)
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" v-model="newPlanItem.durationInput" @input="calculateDuration"
|
||||
placeholder="z.B. 2x7 oder 3*5" style="width:10em" />
|
||||
<input type="number" v-model="newPlanItem.duration" placeholder="Minuten" />
|
||||
<span class="add-plan-item" @click="addPlanItem">+</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Startzeit</th>
|
||||
<th>Aktivität / Zeitblock</th>
|
||||
<th>Gruppe</th>
|
||||
<th>Dauer (Min)</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody ref="sortableList">
|
||||
<template v-for="(item, index) in trainingPlan" :key="item.id">
|
||||
<tr>
|
||||
<td>{{ item.startTime }}</td>
|
||||
<td>
|
||||
<span v-if="item.isTimeblock"><i>Zeitblock</i></span>
|
||||
<span v-else>{{ item.predefinedActivity ? item.predefinedActivity.name :
|
||||
item.activity }}</span>
|
||||
</td>
|
||||
<td>{{ item.groupActivity ? item.groupActivity.name : '' }}</td>
|
||||
<td><span v-if="item.durationText">{{ item.durationText }} / </span>{{ item.duration }}</td>
|
||||
<td><button @click="removePlanItem(item.id)">Entfernen</button></td>
|
||||
</tr>
|
||||
<template v-for="groupItem in item.groupActivities">
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>{{ groupItem.groupPredefinedActivity.name }}</td>
|
||||
<td>{{ groupItem.groupsGroupActivity.name }}</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</template>
|
||||
</template>
|
||||
<tr>
|
||||
<td>{{ calculateNextTime }}</td>
|
||||
<td colspan="4" v-if="!addNewItem && !addNewTimeblock && !addNewGroupActivity">
|
||||
<button @click="openNewPlanItem()">Gesamt-Aktivität</button>
|
||||
<button @click="addTimeblock()">Zeitblock</button>
|
||||
<button v-if="parentIsTimeblock()"
|
||||
@click="addGroupActivity">Gruppen-Aktivität</button>
|
||||
</td>
|
||||
<td v-if="addNewItem || addNewGroupActivity">
|
||||
<input v-if="addtype === 'activity'" type="text" v-model="newPlanItem.activity"
|
||||
placeholder="Aktivität / Zeitblock" required />
|
||||
</td>
|
||||
<td v-else-if="addNewTimeblock">Zeitblock</td>
|
||||
<td v-if="addNewGroupActivity" colspan="2">
|
||||
<select v-model="newPlanItem.groupId">
|
||||
<option v-for="group in groups" :key="group.id" :value="group.id">{{
|
||||
group.name }}</option>
|
||||
</select>
|
||||
</td>
|
||||
<td v-else-if="addNewItem || addNewTimeblock"></td>
|
||||
<td v-if="(addNewItem || addNewTimeblock) && !addNewGroupActivity">
|
||||
<input type="text" v-model="newPlanItem.durationInput"
|
||||
@input="calculateDuration" placeholder="z.B. 2x7 oder 3*5"
|
||||
style="width:10em" />
|
||||
<input type="number" v-model="newPlanItem.duration" placeholder="Minuten" />
|
||||
</td>
|
||||
<td v-else-if="addNewGroupActivity"></td>
|
||||
<td v-if="addNewItem || addNewTimeblock || addNewGroupActivity">
|
||||
<span class="add-plan-item" @click="addPlanItem">+</span>
|
||||
<span class="cancel" @click="cancelAddItem">X</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<button v-if="trainingPlan && trainingPlan.length && trainingPlan.length > 0"
|
||||
@click="generatePDF">Als PDF herunterladen</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="column">
|
||||
<h3>Teilnehmer</h3>
|
||||
<ul>
|
||||
@@ -116,39 +171,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="showNotesModal" class="modal">
|
||||
<div class="modal-content">
|
||||
<span class="close" @click="closeNotesModal">×</span>
|
||||
<h3>Notizen für {{ selectedMember.firstName }} {{ selectedMember.lastName }}</h3>
|
||||
<div>Telefon-Nr.: {{ selectedMember.phone }}</div>
|
||||
<div class="modal-body">
|
||||
<div class="modal-left">
|
||||
<img v-if="selectedMember.imageUrl" :src="selectedMember.imageUrl" alt="Mitgliedsbild"
|
||||
style="width: 250px; height: 250px; object-fit: cover;" />
|
||||
</div>
|
||||
<div class="modal-right">
|
||||
<multiselect v-model="selectedMemberTags" :options="availableTags" placeholder="Tags auswählen"
|
||||
label="name" track-by="id" multiple :close-on-select="true" @tag="addNewTagForMember"
|
||||
@remove="removeMemberTag" @input="updateMemberTags" :allow-empty="false"
|
||||
@keydown.enter.prevent="addNewTagForMemberFromInput" />
|
||||
<div>
|
||||
<textarea v-model="newNoteContent" placeholder="Neue Notiz" rows="4" cols="30"></textarea>
|
||||
<button @click="addMemberNote">Hinzufügen</button>
|
||||
</div>
|
||||
<ul>
|
||||
<li v-for="note in notes" :key="note.id">
|
||||
<button @click="deleteNote(note.id)" class="cancel-action">Löschen</button>
|
||||
{{ note.content }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="showImage" class="memberImage">
|
||||
<img :src="imageUrl" @click="closeImage" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -188,11 +210,21 @@ export default {
|
||||
activity: '',
|
||||
duration: '',
|
||||
durationText: '',
|
||||
groupId: '',
|
||||
isTimeBlock: false,
|
||||
},
|
||||
predefinedActivities: [],
|
||||
showDropdown: false,
|
||||
showImage: false,
|
||||
imageUrl: '',
|
||||
groups: [],
|
||||
currentTimeBlockId: null,
|
||||
newGroupName: '',
|
||||
newGroupLead: '',
|
||||
addtype: 'activity',
|
||||
addNewItem: false,
|
||||
addNewGroupActivity: false,
|
||||
addNewTimeblock: false,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
@@ -206,11 +238,12 @@ export default {
|
||||
computed: {
|
||||
...mapGetters(['isAuthenticated', 'currentClub', 'currentClubName']),
|
||||
calculateNextTime() {
|
||||
let lastTime = this.trainingStart;
|
||||
for (let item of this.trainingPlan) {
|
||||
lastTime = this.addDurationToTime(lastTime, item.duration);
|
||||
this.calculateAllItemTimes();
|
||||
if (this.trainingPlan.length === 0) {
|
||||
return this.trainingStart;
|
||||
}
|
||||
return lastTime;
|
||||
const lastItem = this.trainingPlan[this.trainingPlan.length - 1];
|
||||
return lastItem.endTime;
|
||||
},
|
||||
filteredPredefinedActivities() {
|
||||
const input = this.newPlanItem.activity.toLowerCase();
|
||||
@@ -224,8 +257,8 @@ export default {
|
||||
if (this.isAuthenticated && this.currentClub) {
|
||||
const response = await apiClient.get(`/diary/${this.currentClub}`);
|
||||
this.dates = response.data.map(entry => ({ id: entry.id, date: entry.date }));
|
||||
this.loadTags();
|
||||
this.loadPredefinedActivities();
|
||||
await this.loadTags();
|
||||
await this.loadPredefinedActivities();
|
||||
}
|
||||
},
|
||||
setCurrentDate() {
|
||||
@@ -245,7 +278,6 @@ export default {
|
||||
name: tag.name
|
||||
}));
|
||||
this.previousActivityTags = [...this.selectedActivityTags];
|
||||
|
||||
await this.loadMembers();
|
||||
await this.loadParticipants(dateId);
|
||||
await this.loadActivities(dateId);
|
||||
@@ -254,6 +286,7 @@ export default {
|
||||
.then(response => response.data);
|
||||
|
||||
this.initializeSortable();
|
||||
await this.loadGroups();
|
||||
} else {
|
||||
this.newDate = '';
|
||||
this.trainingStart = '';
|
||||
@@ -275,7 +308,6 @@ export default {
|
||||
trainingStart: this.trainingStart || null,
|
||||
trainingEnd: this.trainingEnd || null,
|
||||
});
|
||||
console.log(response);
|
||||
this.dates.push({ id: response.data.id, date: response.data.date });
|
||||
this.date = { id: response.data.id, date: response.data.date };
|
||||
this.showForm = false;
|
||||
@@ -324,6 +356,14 @@ export default {
|
||||
console.error('Fehler beim Laden der vordefinierten Aktivitäten:', error);
|
||||
}
|
||||
},
|
||||
async loadGroups() {
|
||||
try {
|
||||
const response = await apiClient.get(`/group/${this.currentClub}/${this.date.id}`);
|
||||
this.groups = response.data;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
},
|
||||
isParticipant(memberId) {
|
||||
return this.participants.includes(memberId);
|
||||
},
|
||||
@@ -359,7 +399,7 @@ export default {
|
||||
},
|
||||
async openNotesModal(member) {
|
||||
this.selectedMember = member;
|
||||
await this.loadMemberImage(member);
|
||||
await this.loadMemberImage(member);
|
||||
this.loadMemberNotesAndTags(this.date.id, member.id);
|
||||
this.showNotesModal = true;
|
||||
},
|
||||
@@ -467,7 +507,6 @@ export default {
|
||||
const selectedTags = this.selectedActivityTags;
|
||||
|
||||
if (!selectedTags || !Array.isArray(selectedTags)) {
|
||||
console.log(typeof selectedTags, JSON.stringify(selectedTags));
|
||||
throw new TypeError('Expected selectedTags to be an array');
|
||||
}
|
||||
|
||||
@@ -549,36 +588,95 @@ export default {
|
||||
},
|
||||
async addPlanItem() {
|
||||
try {
|
||||
await apiClient.post(`/diary-date-activities/${this.currentClub}`, {
|
||||
diaryDateId: this.date.id,
|
||||
activity: this.newPlanItem.activity,
|
||||
duration: this.newPlanItem.duration,
|
||||
durationText: this.newPlanItem.durationText,
|
||||
orderId: this.trainingPlan.length
|
||||
});
|
||||
this.newPlanItem = { activity: '', duration: '', durationText: '' };
|
||||
if (this.addNewItem || this.addNewTimeblock) {
|
||||
await apiClient.post(`/diary-date-activities/${this.currentClub}`, {
|
||||
diaryDateId: this.date.id,
|
||||
activity: this.addNewTimeblock ? '' : this.newPlanItem.activity,
|
||||
isTimeblock: this.addNewTimeblock,
|
||||
duration: this.newPlanItem.duration,
|
||||
durationText: this.newPlanItem.durationText,
|
||||
orderId: this.trainingPlan.length
|
||||
});
|
||||
} else if (this.addNewGroupActivity) {//clubId, diaryDateActivityId, groupId, activity
|
||||
await apiClient.post(`/diary-date-activities/group`, {
|
||||
clubId: this.currentClub,
|
||||
diaryDateId: this.date.id,
|
||||
activity: this.newPlanItem.activity,
|
||||
groupId: this.newPlanItem.groupId,
|
||||
});
|
||||
}
|
||||
this.addNewTimeblock = false;
|
||||
this.addNewItem = false;
|
||||
this.newPlanItem = { activity: '', duration: '', durationText: '', groupId: '' };
|
||||
this.trainingPlan = await apiClient.get(`/diary-date-activities/${this.currentClub}/${this.date.id}`).then(response => response.data);
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Hinzufügen des Planungsitems:', error);
|
||||
alert('Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut.');
|
||||
}
|
||||
},
|
||||
async updatePlanItemGroup(planItemId, groupId) {
|
||||
try {
|
||||
await apiClient.put(`/diary-date-activities/${this.currentClub}/${planItemId}/group`, {
|
||||
groupId: groupId
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Aktualisieren der Planungs-Item-Gruppe:', error);
|
||||
}
|
||||
},
|
||||
async removePlanItem(planItemId) {
|
||||
try {
|
||||
await apiClient.delete(`/diary-date-activities/${this.currentClub}`, {
|
||||
params: { planItemId }
|
||||
});
|
||||
this.planItems = this.planItems.filter(item => item.id !== planItemId);
|
||||
await apiClient.delete(`/diary-date-activities/${this.currentClub}/${planItemId}`);
|
||||
this.trainingPlan = this.trainingPlan.filter(item => item.id !== planItemId);
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Entfernen des Planungsitems:', error);
|
||||
}
|
||||
},
|
||||
calculatePlanItemTime(index) {
|
||||
let time = this.trainingStart;
|
||||
for (let i = 0; i < index; i++) {
|
||||
time = this.addDurationToTime(time, this.trainingPlan[i].duration);
|
||||
|
||||
isTimeGreater(time1, time2) {
|
||||
const [hours1, minutes1] = time1.split(':').map(Number);
|
||||
const [hours2, minutes2] = time2.split(':').map(Number);
|
||||
|
||||
if (hours1 > hours2) return true;
|
||||
if (hours1 < hours2) return false;
|
||||
return minutes1 > minutes2;
|
||||
},
|
||||
calculateAllItemTimes() {
|
||||
let currentTime = this.trainingStart;
|
||||
let index = 0;
|
||||
const trainingPlanLength = this.trainingPlan.length;
|
||||
|
||||
while (index < trainingPlanLength) {
|
||||
const currentItem = this.trainingPlan[index];
|
||||
|
||||
if (!currentItem.groupId) {
|
||||
currentItem.startTime = currentTime;
|
||||
currentItem.endTime = this.addDurationToTime(currentItem.startTime, currentItem.duration);
|
||||
currentTime = currentItem.endTime;
|
||||
index += 1;
|
||||
} else {
|
||||
const groupActivities = [];
|
||||
while (index < trainingPlanLength && this.trainingPlan[index].groupId) {
|
||||
groupActivities.push(this.trainingPlan[index]);
|
||||
index += 1;
|
||||
}
|
||||
for (const item of groupActivities) {
|
||||
item.startTime = currentTime;
|
||||
item.endTime = this.addDurationToTime(item.startTime, item.duration);
|
||||
}
|
||||
let latestEndTime = currentTime;
|
||||
for (const item of groupActivities) {
|
||||
if (this.isTimeGreater(item.endTime, latestEndTime)) {
|
||||
latestEndTime = item.endTime;
|
||||
}
|
||||
}
|
||||
currentTime = latestEndTime;
|
||||
}
|
||||
}
|
||||
return time;
|
||||
},
|
||||
|
||||
calculatePlanItemTime(index) {
|
||||
this.calculateAllItemTimes();
|
||||
return this.trainingPlan[index].startTime;
|
||||
},
|
||||
addDurationToTime(startTime, duration) {
|
||||
let [hours, minutes] = startTime.split(':').map(Number);
|
||||
@@ -670,20 +768,57 @@ export default {
|
||||
this.imageUrl = URL.createObjectURL(response.data);
|
||||
} catch (error) {
|
||||
console.error("Failed to load member image:", error);
|
||||
this.imageUrl = null;
|
||||
this.imageUrl = null;
|
||||
}
|
||||
},
|
||||
recalculateTimes() {
|
||||
let currentTime = this.trainingStart; // Die Startzeit des Trainings
|
||||
let currentTime = this.trainingStart;
|
||||
this.trainingPlan.forEach((item, index) => {
|
||||
item.startTime = currentTime;
|
||||
currentTime = this.addDurationToTime(currentTime, item.duration);
|
||||
item.startTime = currentTime;
|
||||
currentTime = this.addDurationToTime(currentTime, item.duration);
|
||||
});
|
||||
},
|
||||
async addGroup() {
|
||||
try {
|
||||
const form = {
|
||||
clubid: this.currentClub,
|
||||
dateid: this.date.id,
|
||||
name: this.newGroupName,
|
||||
lead: this.newGroupLead,
|
||||
}
|
||||
await apiClient.post('/group', form);
|
||||
await this.loadGroups();
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
},
|
||||
parentIsTimeblock() {
|
||||
return this.trainingPlan && this.trainingPlan.length > 0 ? (this.trainingPlan[this.trainingPlan.length - 1].isTimeblock) : false;
|
||||
},
|
||||
openNewPlanItem() {
|
||||
this.addNewItem = true;
|
||||
this.addNewGroupActivity = false;
|
||||
this.addNewTimeblock = false;
|
||||
},
|
||||
cancelAddItem() {
|
||||
this.addNewItem = false;
|
||||
this.addNewGroupActivity = false;
|
||||
this.addNewTimeblock = false;
|
||||
},
|
||||
addTimeblock() {
|
||||
this.addNewTimeblock = true;
|
||||
this.addNewGroupActivity = false;
|
||||
this.addNewItem = false;
|
||||
},
|
||||
async addGroupActivity() {
|
||||
this.addNewGroupActivity = true;
|
||||
this.addNewItem = false;
|
||||
this.addNewTimeblock = false;
|
||||
},
|
||||
},
|
||||
async mounted() {
|
||||
await this.init();
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -703,6 +838,7 @@ button[type="button"] {
|
||||
h3 {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.columns {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
@@ -843,6 +979,22 @@ input[type="number"] {
|
||||
line-height: 1.2em;
|
||||
font-weight: bold;
|
||||
margin-left: 5px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.cancel {
|
||||
border: 1px solid black;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
width: 1.2em;
|
||||
height: 1.2em;
|
||||
text-align: center;
|
||||
line-height: 1.2em;
|
||||
font-weight: bold;
|
||||
margin-left: 5px;
|
||||
color: #ff0000;
|
||||
font-weight: bold;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.add-plan-item:hover {
|
||||
@@ -871,8 +1023,8 @@ input[type="number"] {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.img-icon {
|
||||
cursor: pointer;
|
||||
img {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.memberImage {
|
||||
@@ -886,8 +1038,14 @@ input[type="number"] {
|
||||
overflow: hidden;
|
||||
padding: 3px;
|
||||
}
|
||||
.memberImage > img {
|
||||
|
||||
.memberImage>img {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
.groups {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -142,7 +142,6 @@ export default {
|
||||
},
|
||||
getCurrentClubName() {
|
||||
const club = this.clubs.find(club => club.id === this.currentClub);
|
||||
console.log(club, this.currentClub);
|
||||
return club ? club.name : '';
|
||||
},
|
||||
async generatePDF() {
|
||||
@@ -164,7 +163,6 @@ export default {
|
||||
},
|
||||
getUniqueLocations() {
|
||||
const uniqueLocations = new Map();
|
||||
|
||||
this.matches.forEach(match => {
|
||||
const location = match.location;
|
||||
const clubName = match.homeTeam.name;
|
||||
@@ -173,9 +171,7 @@ export default {
|
||||
location.address,
|
||||
`${location.zip} ${location.city}`
|
||||
];
|
||||
|
||||
const addressKey = addressLines.join('; '); // Unique key für die Map
|
||||
|
||||
const addressKey = addressLines.join('; ');
|
||||
if (!uniqueLocations.has(addressKey)) {
|
||||
uniqueLocations.set(clubName, addressLines);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user