Verbessert die Lesbarkeit und Struktur des Codes in DiaryView.vue durch Anpassungen der Einrückungen und Formatierungen. Optimiert die Anzeige von Aktivitätsvisualisierungen und aktualisiert die Logik für die Eingabefelder, um die Benutzerfreundlichkeit zu erhöhen. Diese Änderungen tragen zur allgemeinen Verbesserung der Benutzeroberfläche und der Codequalität bei.

This commit is contained in:
Torsten Schulz (local)
2025-10-08 11:14:20 +02:00
parent d0ccaa9e54
commit cc08f4ba43

View File

@@ -8,7 +8,8 @@
<option v-for="entry in dates" :key="entry.id" :value="entry">{{ getFormattedDate(entry.date) }}
</option>
</select>
<button v-if="date && date !== 'new' && canDeleteCurrentDate" class="btn-secondary" @click="deleteCurrentDate">Datum löschen</button>
<button v-if="date && date !== 'new' && canDeleteCurrentDate" class="btn-secondary"
@click="deleteCurrentDate">Datum löschen</button>
</label>
</div>
<div v-if="showForm && date === 'new'">
@@ -55,7 +56,7 @@
<ul>
<li v-for="group in groups" :key="group.id">
<span v-if="editingGroupId !== group.id" @click="editGroup(group.id)">{{ group.name
}}</span>
}}</span>
<input v-else type="text" v-model="group.name" @blur="saveGroup(group)"
@keyup.enter="saveGroup(group)" @keyup.esc="cancelEditGroup"
style="display: inline;width:10em" />
@@ -105,51 +106,61 @@
<td>
<span v-if="item.isTimeblock"><i>Zeitblock</i></span>
<span v-else-if="editingActivityId === item.id">
<div style="display: flex; gap: 5px; align-items: center; position: relative;">
<input
type="text"
v-model="editingActivityText"
<div
style="display: flex; gap: 5px; align-items: center; position: relative;">
<input type="text" v-model="editingActivityText"
@input="onEditInputChangeText(item)"
@keyup.enter="saveActivityEdit(item)"
@keyup.esc="cancelActivityEdit"
ref="activityInput"
style="flex: 1;"
/>
<button @click="saveActivityEdit(item)" class="btn-primary" style="padding: 2px 8px; font-size: 12px;"></button>
<button @click="cancelActivityEdit" class="btn-secondary" style="padding: 2px 8px; font-size: 12px;"></button>
<div v-if="editShowDropdown && editSearchForId === item.id && editSearchResults.length" class="dropdown" style="max-height: 9.5em;">
<div v-for="s in editSearchResults" :key="s.id" @click="chooseEditSuggestion(s, item)">
<span v-if="s.code && s.code.trim() !== ''"><strong>[{{ s.name }}]</strong> </span>
@keyup.esc="cancelActivityEdit" ref="activityInput"
style="flex: 1;" />
<button @click="saveActivityEdit(item)" class="btn-primary"
style="padding: 2px 8px; font-size: 12px;"></button>
<button @click="cancelActivityEdit" class="btn-secondary"
style="padding: 2px 8px; font-size: 12px;"></button>
<div v-if="editShowDropdown && editSearchForId === item.id && editSearchResults.length"
class="dropdown" style="max-height: 9.5em;">
<div v-for="s in editSearchResults" :key="s.id"
@click="chooseEditSuggestion(s, item)">
<span v-if="s.code && s.code.trim() !== ''"><strong>[{{
s.name }}]</strong> </span>
</div>
</div>
</div>
</span>
<span v-else @click="startActivityEdit(item)" class="clickable activity-label"
:title="item.predefinedActivity && item.predefinedActivity.name ? item.predefinedActivity.name : ''">
<!-- Icon öffnet Rendering (falls vorhanden) oder Bild im Modal -->
<span v-if="hasActivityVisual(item.predefinedActivity)"
@click.stop="openActivityVisual(item.predefinedActivity)"
class="image-icon"
title="Bild/Zeichnung anzeigen">🖼</span>
{{ (item.predefinedActivity && item.predefinedActivity.code && item.predefinedActivity.code.trim() !== '')
<span v-else @click="startActivityEdit(item)"
class="clickable activity-label"
:title="item.predefinedActivity && item.predefinedActivity.name ? item.predefinedActivity.name : ''">
<!-- Icon öffnet Rendering (falls vorhanden) oder Bild im Modal -->
<span v-if="hasActivityVisual(item.predefinedActivity)"
@click.stop="openActivityVisual(item.predefinedActivity)"
class="image-icon" title="Bild/Zeichnung anzeigen">🖼</span>
{{ (item.predefinedActivity && item.predefinedActivity.code &&
item.predefinedActivity.code.trim() !== '')
? item.predefinedActivity.code
: (item.predefinedActivity ? item.predefinedActivity.name : item.activity) }}
: (item.predefinedActivity ? item.predefinedActivity.name :
item.activity) }}
</span>
</td>
<td>{{ item.groupActivity ? item.groupActivity.name : '' }}</td>
<td>
{{ item.duration }}<span
v-if="item.durationText && item.durationText.trim() !== ''"> ({{
item.durationText }})</span>
item.durationText }})</span>
</td>
<td>
<button @click="toggleActivityMembers(item)" title="Teilnehmer zuordnen" class="person-btn">👤</button>
<button @click="toggleActivityMembers(item)" title="Teilnehmer zuordnen"
class="person-btn">👤</button>
<button @click="removePlanItem(item.id)" class="trash-btn">🗑</button>
<div v-if="activityMembersOpenId === item.id" class="dropdown" style="max-height: 12em; padding: 0.25rem;">
<div style="margin-bottom: 0.25rem; font-weight: 600;">Teilnehmer zuordnen</div>
<div v-if="activityMembersOpenId === item.id" class="dropdown"
style="max-height: 12em; padding: 0.25rem;">
<div style="margin-bottom: 0.25rem; font-weight: 600;">Teilnehmer
zuordnen</div>
<div style="max-height: 9.5em; overflow-y: auto;">
<label v-for="m in presentMembers" :key="m.id" style="display:flex; align-items:center; gap:0.5rem;">
<input type="checkbox" :checked="isAssignedToActivity(item.id, m.id)" @change="toggleMemberForActivity(item.id, m.id, $event.target.checked)">
<label v-for="m in presentMembers" :key="m.id"
style="display:flex; align-items:center; gap:0.5rem;">
<input type="checkbox"
:checked="isAssignedToActivity(item.id, m.id)"
@change="toggleMemberForActivity(item.id, m.id, $event.target.checked)">
<span>{{ m.firstName }} {{ m.lastName }}</span>
</label>
</div>
@@ -161,16 +172,18 @@
<td></td>
<td></td>
<td>
<span class="activity-label" :title="(groupItem.groupPredefinedActivity && groupItem.groupPredefinedActivity.name) ? groupItem.groupPredefinedActivity.name : ''">
<!-- Icon öffnet Rendering (falls vorhanden) oder Bild im Modal -->
<span v-if="hasActivityVisual(groupItem.groupPredefinedActivity)"
@click.stop="openActivityVisual(groupItem.groupPredefinedActivity)"
class="image-icon"
title="Bild/Zeichnung anzeigen">🖼</span>
{{ (groupItem.groupPredefinedActivity && groupItem.groupPredefinedActivity.code && groupItem.groupPredefinedActivity.code.trim() !== '')
<span class="activity-label"
:title="(groupItem.groupPredefinedActivity && groupItem.groupPredefinedActivity.name) ? groupItem.groupPredefinedActivity.name : ''">
<!-- Icon öffnet Rendering (falls vorhanden) oder Bild im Modal -->
<span v-if="hasActivityVisual(groupItem.groupPredefinedActivity)"
@click.stop="openActivityVisual(groupItem.groupPredefinedActivity)"
class="image-icon" title="Bild/Zeichnung anzeigen">🖼</span>
{{ (groupItem.groupPredefinedActivity &&
groupItem.groupPredefinedActivity.code &&
groupItem.groupPredefinedActivity.code.trim() !== '')
? groupItem.groupPredefinedActivity.code
: groupItem.groupPredefinedActivity.name }}
: groupItem.groupPredefinedActivity.name }}
</span>
</td>
<td>{{ groupItem.groupsGroupActivity.name }}</td>
@@ -193,11 +206,13 @@
<div v-if="addtype === 'activity'" style="position: relative;">
<input type="text" v-model="newPlanItem.activity"
placeholder="Aktivität / Zeitblock" required
@input="onNewItemInputChange"
/>
<div v-if="newItemShowDropdown && newItemSearchResults.length" class="dropdown" style="max-height: 9.5em;">
<div v-for="s in newItemSearchResults" :key="s.id" @click="chooseNewItemSuggestion(s)">
<span v-if="s.code && s.code.trim() !== ''"><strong>[{{ s.code }}]</strong> </span>{{ s.name }}
@input="onNewItemInputChange" />
<div v-if="newItemShowDropdown && newItemSearchResults.length"
class="dropdown" style="max-height: 9.5em;">
<div v-for="s in newItemSearchResults" :key="s.id"
@click="chooseNewItemSuggestion(s)">
<span v-if="s.code && s.code.trim() !== ''"><strong>[{{ s.code
}}]</strong> </span>{{ s.name }}
</div>
</div>
</div>
@@ -211,9 +226,8 @@
</td>
<td v-else-if="addNewItem || addNewTimeblock"></td>
<td v-if="(addNewItem || addNewTimeblock) && !addNewGroupActivity">
<input type="text" v-model="newPlanItem.durationText"
@input="calculateDuration" placeholder="z.B. 2x7 oder 3*5"
style="width:10em" />
<input type="text" v-model="newPlanItem.durationText" @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>
@@ -235,7 +249,8 @@
<div v-if="accidents.length > 0">
</div>
</div>
<h3 class="clickable" @click="toggleActivitiesBox">Aktivitäten <span>{{ showActivitiesBox ? '-' : '+' }}</span></h3>
<h3 class="clickable" @click="toggleActivitiesBox">Aktivitäten <span>{{ showActivitiesBox ? '-' :
'+' }}</span></h3>
<div v-if="showActivitiesBox" class="collapsible-box">
<textarea v-model="newActivity"></textarea>
<button @click="addActivity">Aktivität hinzufügen</button>
@@ -244,9 +259,10 @@
{{ 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" :allow-empty="false" @keydown.enter.prevent="addNewTagFromInput" />
<multiselect v-model="selectedActivityTags" :options="availableTags"
placeholder="Tags auswählen" label="name" track-by="id" multiple :close-on-select="true"
@tag="addNewTag" @remove="removeActivityTag" :allow-empty="false"
@keydown.enter.prevent="addNewTagFromInput" />
</div>
<h3>Teilnehmer ({{ participants.length }})</h3>
<ul>
@@ -336,7 +352,8 @@
<label for="memberId">Mitglied:</label>
<select id="memberId" v-model="accident.memberId">
<template v-for="member in members" :key="member.id" :value="member.id">
<option v-if="participants.indexOf(member.id) >= 0" :value="member.id">{{ member.firstName + ' '
<option v-if="participants.indexOf(member.id) >= 0" :value="member.id">{{ member.firstName +
' '
+ member.lastName }}</option>
</template>
</select>
@@ -348,12 +365,13 @@
<button type="button" @click="saveAccident">Eintragen</button>
<button type="button" @click="closeAccidentForm">Schießen</button>
<ul>
<li v-for="accident in accidents" :key="accident.id">{{ accident.firstName + ' ' + accident.lastName +
<li v-for="accident in accidents" :key="accident.id">{{ accident.firstName + ' ' + accident.lastName
+
': '
+ accident.accident}}</li>
</ul>
</form>
</div>
</div>
<!-- Schnell hinzufügen Dialog -->
<div v-if="showQuickAddDialog" class="modal-overlay" @click.self="closeQuickAddDialog">
@@ -531,20 +549,20 @@ export default {
},
isNewMemberValid() {
return this.newMember.firstName.trim() !== '' &&
(this.newMember.gender === 'male' || this.newMember.gender === 'female' || this.newMember.gender === 'diverse');
return this.newMember.firstName.trim() !== '' &&
(this.newMember.gender === 'male' || this.newMember.gender === 'female' || this.newMember.gender === 'diverse');
},
canDeleteCurrentDate() {
if (!this.date || this.date === 'new') return false;
// Prüfe ob keine Inhalte vorhanden sind
const hasTrainingPlan = this.trainingPlan && this.trainingPlan.length > 0;
const hasParticipants = this.participants && this.participants.length > 0;
const hasActivities = this.activities && this.activities.length > 0;
const hasAccidents = this.accidents && this.accidents.length > 0;
const hasNotes = this.notes && this.notes.length > 0;
// Kann gelöscht werden wenn alle Listen leer sind
return !hasTrainingPlan && !hasParticipants && !hasActivities && !hasAccidents && !hasNotes;
},
@@ -560,7 +578,7 @@ export default {
// gerenderter Code / renderSpec
if (pa.renderCode && pa.renderCode.trim() !== '') return true;
if (pa.renderSpec && Object.keys(pa.renderSpec).length) return true;
} catch (e) {}
} catch (e) { }
return false;
},
drawingDataFor(pa) {
@@ -597,10 +615,10 @@ 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 }));
// Automatisch das Datum mit den meisten Einträgen auswählen
await this.autoSelectDateWithEntries();
await this.loadTags();
await this.loadPredefinedActivities();
}
@@ -613,7 +631,7 @@ export default {
}
const today = new Date().toISOString().split('T')[0]; // YYYY-MM-DD Format
// 1. Zuerst prüfe das heutige Datum
const todayEntry = this.dates.find(date => date.date === today);
if (todayEntry) {
@@ -1411,7 +1429,7 @@ export default {
this.playBellSound();
} else {
// Nach letzter Aktivität (Ende) nochmal Glocke
try { this.calculateAllItemTimes(); } catch (e) {}
try { this.calculateAllItemTimes(); } catch (e) { }
const items = Array.isArray(this.trainingPlan) ? this.trainingPlan : [];
const lastItem = items.length ? items[items.length - 1] : null;
const lastEnd = lastItem && lastItem.endTime ? lastItem.endTime : null;
@@ -1423,7 +1441,7 @@ export default {
}
}
// Aktivitätszeiten (Startzeiten) aus Trainingsplan prüfen
try { this.calculateAllItemTimes(); } catch (e) {}
try { this.calculateAllItemTimes(); } catch (e) { }
const items = Array.isArray(this.trainingPlan) ? this.trainingPlan : [];
const startTimesSS = items
.map(it => (it && it.startTime ? (it.startTime.length === 5 ? it.startTime + ':00' : it.startTime) : null))
@@ -1457,7 +1475,7 @@ export default {
calculateIntermediateTimes() {
// Stelle sicher, dass alle startTime-Werte aktuell sind
try { this.calculateAllItemTimes(); } catch(e) {}
try { this.calculateAllItemTimes(); } catch (e) { }
if (!this.trainingPlan || this.trainingPlan.length === 0) {
this.intermediateTimes = [];
return;
@@ -1480,7 +1498,7 @@ export default {
const filtered = unique.filter(t => t !== normalizedStart);
filtered.sort();
this.intermediateTimes = filtered;
},
async addAccident() {
@@ -1522,10 +1540,10 @@ export default {
durationText: item.durationText,
groupId: item.groupId,
});
// Lade die Daten neu, um die Änderungen anzuzeigen
await this.loadTrainingPlan();
this.editingActivityId = null;
this.editingActivityText = '';
} catch (error) {
@@ -1667,12 +1685,12 @@ export default {
// Schnell hinzufügen Dialog Methoden
openQuickAddDialog() {
this.showQuickAddDialog = true;
// Standard-Geburtsdatum: 01.01.(aktuelles Jahr - 10)
const currentYear = new Date().getFullYear();
const defaultBirthYear = currentYear - 10;
const defaultBirthDate = `${defaultBirthYear}-01-01`;
this.newMember = {
firstName: '',
lastName: '',
@@ -1702,7 +1720,7 @@ export default {
const defaultBirthYear = currentYear - 10;
birthDate = `${defaultBirthYear}-01-01`;
}
// Erstelle neues Mitglied
const memberData = {
firstName: this.newMember.firstName.trim(),
@@ -1730,7 +1748,7 @@ export default {
// Lade die aktualisierte Mitgliederliste
const membersResponse = await apiClient.get(`/clubmembers/get/${this.currentClub}/false`);
this.members = membersResponse.data;
// Finde das neu erstellte Mitglied (das letzte in der Liste)
const newMember = this.members[this.members.length - 1];
@@ -1904,7 +1922,11 @@ table {
overflow: visible;
}
thead, tbody, tr, td, th {
thead,
tbody,
tr,
td,
th {
overflow: visible;
}
@@ -1913,7 +1935,8 @@ td {
}
/* Bearbeitungszelle soll relativer Kontext sein */
.clickable, td > div[style*="position: relative"] {
.clickable,
td>div[style*="position: relative"] {
position: relative;
}
@@ -2070,7 +2093,8 @@ img {
margin: 0 auto;
}
.memberImage > div, .memberImage canvas {
.memberImage>div,
.memberImage canvas {
/* falls Komponenten-Inhalt (Renderer) da ist, mittig zeigen */
display: block;
margin: 0 auto;