Fixed time setting in diary - added pdf output for training
This commit is contained in:
@@ -36,11 +36,12 @@ const updateTrainingTimes = async (req, res) => {
|
||||
try {
|
||||
const { clubId } = req.params;
|
||||
const { authcode: userToken } = req.headers;
|
||||
const { date, trainingStart, trainingEnd } = req.body;
|
||||
if (!date || !trainingStart) {
|
||||
const { dateId, trainingStart, trainingEnd } = req.body;
|
||||
if (!dateId || !trainingStart) {
|
||||
console.log(dateId, trainingStart, trainingEnd);
|
||||
throw new HttpError('notallfieldsfilled', 400);
|
||||
}
|
||||
const updatedDate = await diaryService.updateTrainingTimes(userToken, clubId, date, trainingStart, trainingEnd);
|
||||
const updatedDate = await diaryService.updateTrainingTimes(userToken, clubId, dateId, trainingStart, trainingEnd);
|
||||
res.status(200).json(updatedDate);
|
||||
} catch (error) {
|
||||
console.error('[updateTrainingTimes] - Error:', error);
|
||||
|
||||
@@ -54,11 +54,11 @@ class DiaryService {
|
||||
return newDate;
|
||||
}
|
||||
|
||||
async updateTrainingTimes(userToken, clubId, date, trainingStart, trainingEnd) {
|
||||
async updateTrainingTimes(userToken, clubId, dateId, trainingStart, trainingEnd) {
|
||||
console.log('[DiaryService::updateTrainingTimes] - Check user access');
|
||||
await checkAccess(userToken, clubId);
|
||||
console.log('[DiaryService::updateTrainingTimes] - Validate date');
|
||||
const diaryDate = await DiaryDate.findOne({ where: { clubId, date } });
|
||||
const diaryDate = await DiaryDate.findOne({ where: { clubId, id: dateId } });
|
||||
if (!diaryDate) {
|
||||
throw new HttpError('Diary entry not found', 404);
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@ class PDFGenerator {
|
||||
this.pdf = new jsPDF('p', 'mm', 'a4');
|
||||
this.margin = margin;
|
||||
this.columnGap = columnGap;
|
||||
this.pageHeight = 295 - margin * 2; // A4 height in mm minus Ränder
|
||||
this.columnWidth = (210 - margin * 2 - columnGap) / 2; // Zwei Spalten mit Lücke dazwischen
|
||||
this.pageHeight = 295 - margin * 2;
|
||||
this.columnWidth = (210 - margin * 2 - columnGap) / 2;
|
||||
this.position = margin;
|
||||
this.yPos = this.position;
|
||||
this.xPos = margin;
|
||||
@@ -17,14 +17,12 @@ class PDFGenerator {
|
||||
async addSchedule(element) {
|
||||
const canvas = await html2canvas(element, { scale: 2 });
|
||||
const imgData = canvas.toDataURL('image/png');
|
||||
const imgWidth = 210 - this.margin * 2; // A4 width in mm minus Ränder
|
||||
const imgWidth = 210 - this.margin * 2;
|
||||
const imgHeight = (canvas.height * imgWidth) / canvas.width;
|
||||
let heightLeft = imgHeight;
|
||||
let position = this.margin;
|
||||
|
||||
this.pdf.addImage(imgData, 'PNG', this.margin, position, imgWidth, imgHeight);
|
||||
heightLeft -= this.pageHeight;
|
||||
|
||||
while (heightLeft >= 0) {
|
||||
position = heightLeft - imgHeight + this.margin;
|
||||
this.pdf.addPage();
|
||||
@@ -52,8 +50,6 @@ class PDFGenerator {
|
||||
|
||||
addAddress(clubName, addressLines) {
|
||||
this.pdf.setFontSize(10);
|
||||
|
||||
// Vereinname fett drucken
|
||||
this.pdf.setFont('helvetica', 'bold');
|
||||
this.pdf.text(clubName, this.xPos, this.yPos);
|
||||
this.yPos += 5;
|
||||
@@ -63,30 +59,76 @@ class PDFGenerator {
|
||||
this.pdf.text(line, this.xPos, this.yPos);
|
||||
this.yPos += 5;
|
||||
});
|
||||
|
||||
this.yPos += 10; // Abstand zwischen den Adressen
|
||||
|
||||
// Spaltenwechsel oder neuer Seite bei Bedarf
|
||||
this.yPos += 10;
|
||||
this.checkColumnOverflow();
|
||||
}
|
||||
|
||||
checkColumnOverflow() {
|
||||
if (this.isLeftColumn) {
|
||||
if (this.yPos > this.pageHeight) {
|
||||
this.xPos += this.columnWidth + this.columnGap; // Zur rechten Spalte wechseln
|
||||
this.yPos = this.position; // Zurück zum Anfang der neuen Spalte
|
||||
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; // Zurück zur linken Spalte auf der neuen Seite
|
||||
this.xPos = this.margin;
|
||||
this.yPos = this.position;
|
||||
this.isLeftColumn = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addTrainingPlan(clubName, trainingDate, trainingStart, trainingEnd, trainingPlan) {
|
||||
this.pdf.setFontSize(14);
|
||||
this.pdf.setFont('helvetica', 'bold');
|
||||
this.pdf.text(`${clubName} - Trainingsplan`, this.margin, this.yPos);
|
||||
this.yPos += 10;
|
||||
this.pdf.setFontSize(12);
|
||||
this.pdf.setFont('helvetica', 'normal');
|
||||
this.pdf.text(`Datum: ${trainingDate}`, this.margin, this.yPos);
|
||||
this.yPos += 7;
|
||||
this.pdf.text(`Uhrzeit: ${trainingStart} - ${trainingEnd}`, this.margin, this.yPos);
|
||||
this.yPos += 10;
|
||||
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.setFont('helvetica', 'normal');
|
||||
trainingPlan.forEach((item, index) => {
|
||||
const time = this.calculatePlanItemTime(index, trainingStart, trainingPlan);
|
||||
this.pdf.text(time, this.margin, this.yPos);
|
||||
this.pdf.text(item.activity, 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();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
calculatePlanItemTime(index, startTime, trainingPlan) {
|
||||
let time = startTime;
|
||||
for (let i = 0; i < index; i++) {
|
||||
time = this.addDurationToTime(time, trainingPlan[i].duration);
|
||||
}
|
||||
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;
|
||||
}
|
||||
hours = hours % 24;
|
||||
return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`;
|
||||
}
|
||||
|
||||
save(filename) {
|
||||
this.pdf.save(filename);
|
||||
}
|
||||
|
||||
@@ -3,13 +3,14 @@
|
||||
<h2>Trainingstagebuch</h2>
|
||||
<div>
|
||||
<label>Datum:
|
||||
<select v-model="date" @change="handleDateChange">
|
||||
<select v-model="selectedDate" @change="handleDateChange">
|
||||
<option value="new">Neu anlegen</option>
|
||||
<option v-for="entry in dates" :key="entry.id" :value="entry">{{ entry.date }} </option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
<div v-if="showForm && date === 'new'">
|
||||
|
||||
<div v-if="showForm && selectedDate === 'new'">
|
||||
<h3>Neues Datum anlegen</h3>
|
||||
<form @submit.prevent="createDate">
|
||||
<div>
|
||||
@@ -28,7 +29,8 @@
|
||||
<button type="submit">Datum anlegen</button>
|
||||
</form>
|
||||
</div>
|
||||
<div v-if="!showForm && date !== null && date !== 'new'">
|
||||
|
||||
<div v-if="!showForm && selectedDate !== null && selectedDate !== 'new'">
|
||||
<h3>Trainingszeiten bearbeiten</h3>
|
||||
<form @submit.prevent="updateTrainingTimes">
|
||||
<div>
|
||||
@@ -43,7 +45,7 @@
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div v-if="date !== 'new' && date !== null">
|
||||
<div v-if="selectedDate !== 'new' && selectedDate !== null">
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<h3>Trainingsplan</h3>
|
||||
@@ -60,7 +62,7 @@
|
||||
<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.activity }}</td>
|
||||
<td>
|
||||
<span @click="removePlanItem(planItem.id)" class="add-plan-item">-</span>
|
||||
{{ planItem.duration }}
|
||||
@@ -76,7 +78,7 @@
|
||||
<div v-for="activity in filteredPredefinedActivities" :key="activity.id"
|
||||
@click="selectPredefinedActivity(activity)">
|
||||
{{ activity.name }} ({{ activity.durationText || '' }} / {{
|
||||
activity.duration }} Minuten)
|
||||
activity.duration }} Minuten)
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
@@ -89,61 +91,7 @@
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<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)">
|
||||
<span @click="openNotesModal(member)" class="clickable">{{ member.firstName }} {{
|
||||
member.lastName }}</span>
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
<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>
|
||||
<multiselect v-model="selectedActivityTags" :options="availableTags" placeholder="Tags auswählen"
|
||||
label="name" track-by="id" multiple :close-on-select="true" @tag="addNewTag"
|
||||
@remove="removeActivityTag" @input="updateActivityTags" :allow-empty="false"
|
||||
@keydown.enter.prevent="addNewTagFromInput" />
|
||||
</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 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>
|
||||
<button @click="generatePDF">PDF speichern</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -153,33 +101,19 @@
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import apiClient from '../apiClient.js';
|
||||
import Multiselect from 'vue-multiselect';
|
||||
import Sortable from 'sortablejs';
|
||||
import PDFGenerator from '../components/PDFGenerator.js';
|
||||
|
||||
export default {
|
||||
name: 'DiaryView',
|
||||
components: { Multiselect },
|
||||
data() {
|
||||
return {
|
||||
date: null,
|
||||
selectedDate: null,
|
||||
dates: [],
|
||||
showForm: false,
|
||||
newDate: '',
|
||||
trainingStart: '',
|
||||
trainingEnd: '',
|
||||
members: [],
|
||||
participants: [],
|
||||
newActivity: '',
|
||||
activities: [],
|
||||
notes: [],
|
||||
newNoteContent: '',
|
||||
selectedMember: null,
|
||||
showNotesModal: false,
|
||||
selectedActivityTags: [],
|
||||
selectedMemberTags: [],
|
||||
availableTags: [],
|
||||
previousActivityTags: [],
|
||||
previousMemberTags: [],
|
||||
trainingPlan: [],
|
||||
newPlanItem: {
|
||||
activity: '',
|
||||
@@ -190,14 +124,6 @@ export default {
|
||||
showDropdown: false,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
selectedMemberTags(newTags) {
|
||||
this.updateMemberTags(newTags);
|
||||
},
|
||||
selectedActivityTags(newTags) {
|
||||
this.updateActivityTags(newTags);
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['isAuthenticated', 'currentClub']),
|
||||
calculateNextTime() {
|
||||
@@ -219,7 +145,6 @@ 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();
|
||||
}
|
||||
},
|
||||
@@ -228,24 +153,15 @@ export default {
|
||||
this.newDate = today;
|
||||
},
|
||||
async handleDateChange() {
|
||||
this.showForm = this.date === 'new';
|
||||
if (this.date && this.date !== 'new') {
|
||||
const dateId = this.date.id;
|
||||
const response = await apiClient.get(`/diary/${this.currentClub}`);
|
||||
const dateData = response.data.find(entry => entry.id === dateId);
|
||||
this.showForm = this.selectedDate === 'new';
|
||||
if (this.selectedDate && this.selectedDate !== 'new') {
|
||||
const dateId = this.selectedDate.id;
|
||||
const response = await apiClient.get(`/diary/${this.currentClub}/${dateId}`);
|
||||
const dateData = response.data;
|
||||
this.trainingStart = dateData.trainingStart;
|
||||
this.trainingEnd = dateData.trainingEnd;
|
||||
this.selectedActivityTags = dateData.diaryTags.map(tag => ({
|
||||
id: tag.id,
|
||||
name: tag.name
|
||||
}));
|
||||
this.previousActivityTags = [...this.selectedActivityTags]; // Hier setzen
|
||||
|
||||
await this.loadMembers();
|
||||
await this.loadParticipants(dateId);
|
||||
await this.loadActivities(dateId);
|
||||
this.trainingPlan = await apiClient
|
||||
.get(`/diary-date-activities/${this.currentClub}/${this.date.id}`)
|
||||
.get(`/diary-date-activities/${this.currentClub}/${dateId}`)
|
||||
.then(response => response.data);
|
||||
|
||||
this.initializeSortable();
|
||||
@@ -253,7 +169,6 @@ export default {
|
||||
this.newDate = '';
|
||||
this.trainingStart = '';
|
||||
this.trainingEnd = '';
|
||||
this.participants = [];
|
||||
}
|
||||
},
|
||||
initializeSortable() {
|
||||
@@ -271,7 +186,7 @@ export default {
|
||||
trainingEnd: this.trainingEnd || null,
|
||||
});
|
||||
this.dates.push({ id: response.data.id, date: response.data.date });
|
||||
this.date = { id: response.data.id, date: response.data.date };
|
||||
this.selectedDate = { id: response.data.id, date: response.data.date };
|
||||
this.showForm = false;
|
||||
this.newDate = '';
|
||||
this.trainingStart = '';
|
||||
@@ -282,7 +197,7 @@ export default {
|
||||
},
|
||||
async updateTrainingTimes() {
|
||||
try {
|
||||
const dateId = this.date.id;
|
||||
const dateId = this.selectedDate.id;
|
||||
await apiClient.put(`/diary/${this.currentClub}`, {
|
||||
dateId,
|
||||
trainingStart: this.trainingStart || null,
|
||||
@@ -294,22 +209,6 @@ 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);
|
||||
},
|
||||
async loadActivities(dateId) {
|
||||
const response = await apiClient.get(`/activities/${dateId}`);
|
||||
this.activities = response.data;
|
||||
},
|
||||
async loadTags() {
|
||||
const response = await apiClient.get('/tags');
|
||||
this.availableTags = response.data;
|
||||
},
|
||||
async loadPredefinedActivities() {
|
||||
try {
|
||||
const response = await apiClient.get('/predefined-activities');
|
||||
@@ -318,216 +217,6 @@ export default {
|
||||
console.error('Fehler beim Laden der vordefinierten Aktivitäten:', error);
|
||||
}
|
||||
},
|
||||
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 addActivity() {
|
||||
const dateId = this.date.id;
|
||||
if (this.newActivity) {
|
||||
const response = await apiClient.post('/activities/add', {
|
||||
diaryDateId: dateId,
|
||||
description: this.newActivity,
|
||||
tags: this.selectedActivityTags.map(tag => tag.id)
|
||||
});
|
||||
this.activities.push(response.data);
|
||||
this.newActivity = '';
|
||||
this.selectedActivityTags = [];
|
||||
}
|
||||
},
|
||||
async openNotesModal(member) {
|
||||
this.selectedMember = member;
|
||||
await this.loadMemberImage(member);
|
||||
this.loadMemberNotesAndTags(this.date.id, member.id);
|
||||
this.showNotesModal = true;
|
||||
},
|
||||
async loadMemberNotesAndTags(diaryDateId, memberId) {
|
||||
try {
|
||||
const notesResponse = await apiClient.get(`/diarymember/${this.currentClub}/note`, {
|
||||
params: { diaryDateId, memberId }
|
||||
});
|
||||
this.notes = notesResponse.data;
|
||||
const tagsResponse = await apiClient.get(`/diarymember/${this.currentClub}/tag`, {
|
||||
params: { diaryDateId, memberId }
|
||||
});
|
||||
this.selectedMemberTags = tagsResponse.data.map(tag => ({
|
||||
id: tag.tag.id,
|
||||
name: tag.tag.name
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error('Error loading member notes and tags:', error);
|
||||
alert('Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut.');
|
||||
}
|
||||
},
|
||||
async addMemberNote() {
|
||||
if (this.newNoteContent) {
|
||||
const response = await apiClient.post(`/diarymember/${this.currentClub}/note`, {
|
||||
memberId: this.selectedMember.id,
|
||||
diaryDateId: this.date.id,
|
||||
content: this.newNoteContent
|
||||
});
|
||||
this.notes = response.data;
|
||||
this.newNoteContent = '';
|
||||
this.selectedTagsNotes = [];
|
||||
}
|
||||
},
|
||||
async deleteNote(noteId) {
|
||||
const response = await apiClient.delete(`/diarymember/${this.currentClub}/note/${noteId}`, {
|
||||
clubId: this.currentClub
|
||||
});
|
||||
this.notes = response.data;
|
||||
},
|
||||
closeNotesModal() {
|
||||
this.showNotesModal = false;
|
||||
},
|
||||
async addNewTagFromInput(event) {
|
||||
const inputValue = event.target.value.trim();
|
||||
if (inputValue) {
|
||||
await this.addNewTag(inputValue);
|
||||
}
|
||||
},
|
||||
async addNewTag(newTagName) {
|
||||
try {
|
||||
const response = await apiClient.post('/tags', { name: newTagName });
|
||||
const newTag = response.data;
|
||||
this.availableTags.push(newTag);
|
||||
this.selectedActivityTags.push(newTag);
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Hinzufügen eines neuen Tags:', error);
|
||||
alert('Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut.');
|
||||
}
|
||||
},
|
||||
async addNewTagForMemberFromInput(event) {
|
||||
const inputValue = event.target.value.trim();
|
||||
if (inputValue) {
|
||||
await this.addNewTagForMember(inputValue);
|
||||
}
|
||||
},
|
||||
async addNewTagForMember(newTagName) {
|
||||
try {
|
||||
const response = await apiClient.post('/tags', { name: newTagName });
|
||||
const newTag = response.data;
|
||||
this.availableTags.push(newTag);
|
||||
this.selectedMemberTags.push(newTag);
|
||||
await this.linkTagToMemberAndDate(newTag);
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Hinzufügen eines neuen Tags für das Mitglied:', error);
|
||||
alert('Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut.');
|
||||
}
|
||||
},
|
||||
async linkTagToDiaryDate(tag) {
|
||||
try {
|
||||
const tagId = tag.id;
|
||||
await apiClient.post(`/diary/tag/${this.currentClub}/add-tag`, {
|
||||
diaryDateId: this.date.id,
|
||||
tagId: tagId
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Verknüpfen des Tags mit dem Trainingstag:', error);
|
||||
alert('Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut.');
|
||||
}
|
||||
},
|
||||
async linkTagToMemberAndDate(tag) {
|
||||
try {
|
||||
const tagId = tag.id;
|
||||
await apiClient.post(`/diarymember/${this.currentClub}/tag`, {
|
||||
diaryDateId: this.date.id,
|
||||
memberId: this.selectedMember.id,
|
||||
tagId: tagId
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Verknüpfen des Tags mit dem Mitglied und Datum:', error);
|
||||
alert('Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut.');
|
||||
}
|
||||
},
|
||||
async updateActivityTags() {
|
||||
try {
|
||||
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');
|
||||
}
|
||||
|
||||
for (let tag of selectedTags) {
|
||||
if (!this.previousActivityTags.includes(tag)) {
|
||||
await this.linkTagToDiaryDate(tag);
|
||||
}
|
||||
}
|
||||
|
||||
this.previousActivityTags = [...selectedTags];
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Verknüpfen der Tags mit dem Trainingstag:', error);
|
||||
alert('Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut.');
|
||||
}
|
||||
},
|
||||
async updateMemberTags() {
|
||||
try {
|
||||
for (let tag of this.selectedMemberTags) {
|
||||
if (!this.previousMemberTags.includes(tag)) {
|
||||
await this.linkTagToMemberAndDate(tag);
|
||||
}
|
||||
}
|
||||
this.previousMemberTags = [...this.selectedMemberTags];
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Verknüpfen der Tags mit dem Mitglied und Datum:', error);
|
||||
alert('Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut.');
|
||||
}
|
||||
},
|
||||
async removeMemberTag(tagId) {
|
||||
try {
|
||||
await apiClient.post(`/diarymember/${this.currentClub}/tag/remove`, {
|
||||
diaryDateId: this.date.id,
|
||||
memberId: this.selectedMember.id,
|
||||
tagId: tagId
|
||||
});
|
||||
this.selectedMemberTags = this.selectedMemberTags.filter(tag => tag.id !== tagId);
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Entfernen des Tags:', error);
|
||||
alert('Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut.');
|
||||
}
|
||||
},
|
||||
async removeMemberNote(noteContent) {
|
||||
try {
|
||||
await apiClient.post(`/diarymember/${this.currentClub}/note/remove`, {
|
||||
diaryDateId: this.date.id,
|
||||
memberId: this.selectedMember.id,
|
||||
content: noteContent
|
||||
});
|
||||
this.notes = this.notes.filter(note => note.content !== noteContent);
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Entfernen der Notiz:', error);
|
||||
alert('Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut.');
|
||||
}
|
||||
},
|
||||
async removeActivityTag(tag) {
|
||||
try {
|
||||
const tagId = tag.id;
|
||||
await apiClient.delete(`/diary/${this.currentClub}/tag`, {
|
||||
params: { tagId }
|
||||
});
|
||||
this.selectedActivityTags = this.selectedActivityTags.filter(t => t.id !== tagId);
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Entfernen des Tags:', error);
|
||||
alert('Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut.');
|
||||
}
|
||||
},
|
||||
handleActivityInput() {
|
||||
if (this.newPlanItem.activity) {
|
||||
this.showDropdown = true;
|
||||
@@ -544,14 +233,14 @@ export default {
|
||||
async addPlanItem() {
|
||||
try {
|
||||
await apiClient.post(`/diary-date-activities/${this.currentClub}`, {
|
||||
diaryDateId: this.date.id,
|
||||
diaryDateId: this.selectedDate.id,
|
||||
activity: this.newPlanItem.activity,
|
||||
duration: this.newPlanItem.duration,
|
||||
durationText: this.newPlanItem.durationText,
|
||||
orderId: this.trainingPlan.length
|
||||
});
|
||||
this.newPlanItem = { activity: '', duration: '', durationText: '' };
|
||||
this.trainingPlan = await apiClient.get(`/diary-date-activities/${this.currentClub}/${this.date.id}`).then(response => response.data);
|
||||
this.trainingPlan = await apiClient.get(`/diary-date-activities/${this.currentClub}/${this.selectedDate.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.');
|
||||
@@ -559,10 +248,8 @@ export default {
|
||||
},
|
||||
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);
|
||||
}
|
||||
@@ -575,39 +262,18 @@ export default {
|
||||
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;
|
||||
}
|
||||
hours = hours % 24;
|
||||
return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`;
|
||||
},
|
||||
calculateDuration() {
|
||||
const input = this.newPlanItem.durationInput;
|
||||
let calculatedDuration = 0;
|
||||
const multiplyPattern = /(\d+)\s*[x*]\s*(\d+)/i;
|
||||
const match = input.match(multiplyPattern);
|
||||
if (match) {
|
||||
const [, num1, num2] = match;
|
||||
calculatedDuration = parseInt(num1) * parseInt(num2);
|
||||
} else if (!isNaN(input)) {
|
||||
calculatedDuration = parseInt(input);
|
||||
}
|
||||
calculatedDuration = Math.ceil(calculatedDuration / 5) * 5;
|
||||
if (!this.newPlanItem.durationText || this.newPlanItem.durationText === input) {
|
||||
this.newPlanItem.duration = calculatedDuration;
|
||||
this.newPlanItem = { ...this.newPlanItem, duration: calculatedDuration };
|
||||
}
|
||||
},
|
||||
async removePlanItem(planItemId) {
|
||||
try {
|
||||
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);
|
||||
alert('Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut.');
|
||||
let [hours, minutes] = startTime.split(':').map(Number);
|
||||
minutes += Number(duration);
|
||||
if (minutes >= 60) {
|
||||
hours += Math.floor(minutes / 60);
|
||||
minutes = minutes % 60;
|
||||
}
|
||||
hours = hours % 24;
|
||||
return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`;
|
||||
} catch(error) {
|
||||
console.log(error);
|
||||
return '';
|
||||
}
|
||||
},
|
||||
async onDragEnd(evt) {
|
||||
@@ -621,21 +287,18 @@ export default {
|
||||
alert('Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut.');
|
||||
}
|
||||
},
|
||||
async loadMemberImage(member) {
|
||||
try {
|
||||
const response = await apiClient.get(`/clubmembers/${this.currentClub}/image/${member.id}`, {
|
||||
responseType: 'blob',
|
||||
});
|
||||
const imageUrl = URL.createObjectURL(response.data);
|
||||
member.imageUrl = imageUrl;
|
||||
} catch (error) {
|
||||
console.error("Failed to load member image:", error);
|
||||
member.imageUrl = null;
|
||||
}
|
||||
async generatePDF() {
|
||||
const clubName = this.$store.getters.currentClubName;
|
||||
const trainingDate = this.selectedDate.date;
|
||||
const trainingStart = this.trainingStart;
|
||||
const trainingEnd = this.trainingEnd;
|
||||
|
||||
const pdfGenerator = new PDFGenerator();
|
||||
pdfGenerator.addTrainingPlan(clubName, trainingDate, trainingStart, trainingEnd, this.trainingPlan);
|
||||
pdfGenerator.save('Trainingsplan.pdf');
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
|
||||
await this.init();
|
||||
}
|
||||
};
|
||||
@@ -674,62 +337,6 @@ ul {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.modal {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: fixed;
|
||||
z-index: 1000;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 50%;
|
||||
height: 50%;
|
||||
overflow: auto;
|
||||
background-color: rgba(200, 200, 200, 0.5);
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background-color: #fefefe;
|
||||
padding: 20px;
|
||||
border: 1px solid #555;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
box-shadow: 4px 3px 2px #999;
|
||||
}
|
||||
|
||||
.close {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 15px;
|
||||
color: #aaa;
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.close:hover,
|
||||
.close:focus {
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
padding: 0;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.multiselect {
|
||||
margin-bottom: 10px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
@@ -779,11 +386,6 @@ input[type="number"] {
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
|
||||
.clickable {
|
||||
cursor: pointer;
|
||||
color: #45a049;
|
||||
}
|
||||
|
||||
.add-plan-item {
|
||||
border: 1px solid black;
|
||||
cursor: pointer;
|
||||
@@ -804,21 +406,4 @@ input[type="number"] {
|
||||
.drag-handle {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.modal-left {
|
||||
flex: 0 0 250px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
.modal-right {
|
||||
flex: 1;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user