From 40dcd0e54c78b01d8a9efa67aac5483a26af95b0 Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Wed, 8 Oct 2025 12:49:42 +0200 Subject: [PATCH] Refactor modals in DiaryView, MembersView, OfficialTournaments, ScheduleView, and TrainingStatsView to use dedicated dialog components for improved maintainability and user experience. Update styles and structure for consistency across the application. --- .../src/components/AccidentFormDialog.vue | 221 +++++++++++++++ frontend/src/components/BaseDialog.vue | 4 +- frontend/src/components/CsvImportDialog.vue | 185 +++++++++++++ frontend/src/components/MemberNotesDialog.vue | 249 +++++++++++++++++ .../src/components/MemberSelectionDialog.vue | 260 ++++++++++++++++++ .../src/components/QuickAddMemberDialog.vue | 208 ++++++++++++++ frontend/src/components/TagHistoryDialog.vue | 154 +++++++++++ .../src/components/TrainingDetailsDialog.vue | 222 +++++++++++++++ frontend/src/views/DiaryView.vue | 259 +++++++++-------- frontend/src/views/MembersView.vue | 54 ++-- frontend/src/views/OfficialTournaments.vue | 106 ++++--- frontend/src/views/ScheduleView.vue | 32 +-- frontend/src/views/TrainingStatsView.vue | 47 +--- 13 files changed, 1776 insertions(+), 225 deletions(-) create mode 100644 frontend/src/components/AccidentFormDialog.vue create mode 100644 frontend/src/components/CsvImportDialog.vue create mode 100644 frontend/src/components/MemberNotesDialog.vue create mode 100644 frontend/src/components/MemberSelectionDialog.vue create mode 100644 frontend/src/components/QuickAddMemberDialog.vue create mode 100644 frontend/src/components/TagHistoryDialog.vue create mode 100644 frontend/src/components/TrainingDetailsDialog.vue diff --git a/frontend/src/components/AccidentFormDialog.vue b/frontend/src/components/AccidentFormDialog.vue new file mode 100644 index 0000000..b37603b --- /dev/null +++ b/frontend/src/components/AccidentFormDialog.vue @@ -0,0 +1,221 @@ + + + + + + diff --git a/frontend/src/components/BaseDialog.vue b/frontend/src/components/BaseDialog.vue index 3a46833..b96c78d 100644 --- a/frontend/src/components/BaseDialog.vue +++ b/frontend/src/components/BaseDialog.vue @@ -294,7 +294,7 @@ export default { .dialog-header { background: linear-gradient(135deg, var(--primary-color), var(--primary-hover)); color: white; - padding: 12px 16px; + padding: 4px 16px; display: flex; justify-content: space-between; align-items: center; @@ -353,7 +353,7 @@ export default { /* Footer */ .dialog-footer { - padding: 12px 16px; + padding: 4px 16px; border-top: 1px solid var(--border-color); display: flex; justify-content: flex-end; diff --git a/frontend/src/components/CsvImportDialog.vue b/frontend/src/components/CsvImportDialog.vue new file mode 100644 index 0000000..23ffaad --- /dev/null +++ b/frontend/src/components/CsvImportDialog.vue @@ -0,0 +1,185 @@ + + + + + + diff --git a/frontend/src/components/MemberNotesDialog.vue b/frontend/src/components/MemberNotesDialog.vue new file mode 100644 index 0000000..863bacd --- /dev/null +++ b/frontend/src/components/MemberNotesDialog.vue @@ -0,0 +1,249 @@ + + + + + + diff --git a/frontend/src/components/MemberSelectionDialog.vue b/frontend/src/components/MemberSelectionDialog.vue new file mode 100644 index 0000000..e2dbaf3 --- /dev/null +++ b/frontend/src/components/MemberSelectionDialog.vue @@ -0,0 +1,260 @@ + + + + + + diff --git a/frontend/src/components/QuickAddMemberDialog.vue b/frontend/src/components/QuickAddMemberDialog.vue new file mode 100644 index 0000000..23635be --- /dev/null +++ b/frontend/src/components/QuickAddMemberDialog.vue @@ -0,0 +1,208 @@ + + + + + + diff --git a/frontend/src/components/TagHistoryDialog.vue b/frontend/src/components/TagHistoryDialog.vue new file mode 100644 index 0000000..59012c8 --- /dev/null +++ b/frontend/src/components/TagHistoryDialog.vue @@ -0,0 +1,154 @@ + + + + + + diff --git a/frontend/src/components/TrainingDetailsDialog.vue b/frontend/src/components/TrainingDetailsDialog.vue new file mode 100644 index 0000000..be349ad --- /dev/null +++ b/frontend/src/components/TrainingDetailsDialog.vue @@ -0,0 +1,222 @@ + + + + + + diff --git a/frontend/src/views/DiaryView.vue b/frontend/src/views/DiaryView.vue index 1aa1985..67bc258 100644 --- a/frontend/src/views/DiaryView.vue +++ b/frontend/src/views/DiaryView.vue @@ -289,55 +289,31 @@ - + + - + + -
-
-
- - -
-
- - -
- - -
    -
  • {{ accident.firstName + ' ' + accident.lastName - + - ': ' - + accident.accident}}
  • -
-
-
+ + - + @@ -457,6 +383,10 @@ import InfoDialog from '../components/InfoDialog.vue'; import ConfirmDialog from '../components/ConfirmDialog.vue'; import ImageDialog from '../components/ImageDialog.vue'; import BaseDialog from '../components/BaseDialog.vue'; +import MemberNotesDialog from '../components/MemberNotesDialog.vue'; +import TagHistoryDialog from '../components/TagHistoryDialog.vue'; +import AccidentFormDialog from '../components/AccidentFormDialog.vue'; +import QuickAddMemberDialog from '../components/QuickAddMemberDialog.vue'; export default { name: 'DiaryView', @@ -466,7 +396,11 @@ export default { InfoDialog, ConfirmDialog, ImageDialog, - BaseDialog + BaseDialog, + MemberNotesDialog, + TagHistoryDialog, + AccidentFormDialog, + QuickAddMemberDialog }, data() { return { @@ -2426,4 +2360,99 @@ img { cursor: not-allowed !important; opacity: 0.6 !important; } + +/* Notes Modal Styles */ +.notes-modal-content { + display: flex; + flex-direction: column; + gap: 1rem; +} + +.notes-header-info { + padding: 0.5rem; + background: var(--background-light); + border-radius: 4px; + font-size: 0.9rem; + color: var(--text-muted); +} + +.notes-body { + display: flex; + gap: 1.5rem; +} + +.notes-left { + flex-shrink: 0; +} + +.member-image { + width: 250px; + height: 250px; + object-fit: cover; + border-radius: 8px; + border: 1px solid var(--border-color); +} + +.notes-right { + flex: 1; + display: flex; + flex-direction: column; + gap: 1rem; +} + +.note-textarea { + width: 100%; + padding: 0.5rem; + border: 1px solid var(--border-color); + border-radius: 4px; + font-family: inherit; + resize: vertical; +} + +.notes-list h4 { + margin: 0 0 0.5rem 0; + font-size: 1rem; +} + +.notes-list ul { + list-style: none; + padding: 0; + margin: 0; +} + +.note-item { + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0.5rem; + margin-bottom: 0.5rem; + background: var(--background-light); + border-radius: 4px; +} + +.note-content { + flex: 1; +} + +@media (max-width: 768px) { + .notes-body { + flex-direction: column; + } + + .member-image { + width: 100%; + height: auto; + max-height: 300px; + } +} + +/* Render Container */ +.render-container { + display: flex; + justify-content: center; + align-items: center; + padding: 1rem; + background: #f5f5f5; + border-radius: 4px; +} diff --git a/frontend/src/views/MembersView.vue b/frontend/src/views/MembersView.vue index 5037cf5..aacea9c 100644 --- a/frontend/src/views/MembersView.vue +++ b/frontend/src/views/MembersView.vue @@ -138,22 +138,18 @@ @rotate="handleRotate" /> - + + @@ -186,12 +182,16 @@ import PDFGenerator from '../components/PDFGenerator.js'; import InfoDialog from '../components/InfoDialog.vue'; import ConfirmDialog from '../components/ConfirmDialog.vue'; import ImageViewerDialog from '../components/ImageViewerDialog.vue'; +import BaseDialog from '../components/BaseDialog.vue'; +import MemberNotesDialog from '../components/MemberNotesDialog.vue'; export default { name: 'MembersView', components: { InfoDialog, ConfirmDialog, - ImageViewerDialog + ImageViewerDialog, + BaseDialog, + MemberNotesDialog }, computed: { ...mapGetters(['isAuthenticated', 'currentClub']), @@ -790,4 +790,24 @@ table td { .rotate-btn:active { transform: translateY(0); } + +/* Dialog-spezifische Styles */ +.member-notes-content { + min-height: 200px; +} + +.btn-primary { + padding: 0.5rem 1rem; + border: none; + border-radius: 4px; + background: linear-gradient(135deg, var(--primary-color), var(--primary-hover)); + color: white; + font-weight: 600; + cursor: pointer; + transition: opacity 0.2s; +} + +.btn-primary:hover { + opacity: 0.9; +} diff --git a/frontend/src/views/OfficialTournaments.vue b/frontend/src/views/OfficialTournaments.vue index d5d29ed..fd95321 100644 --- a/frontend/src/views/OfficialTournaments.vue +++ b/frontend/src/views/OfficialTournaments.vue @@ -381,44 +381,21 @@ - + + @@ -450,8 +427,16 @@ import PDFGenerator from '../components/PDFGenerator.js'; import InfoDialog from '../components/InfoDialog.vue'; import ConfirmDialog from '../components/ConfirmDialog.vue'; +import BaseDialog from '../components/BaseDialog.vue'; +import MemberSelectionDialog from '../components/MemberSelectionDialog.vue'; export default { name: 'OfficialTournaments', + components: { + InfoDialog, + ConfirmDialog, + BaseDialog, + MemberSelectionDialog + }, data() { return { // Dialog States @@ -1032,6 +1017,32 @@ export default { }, selectAllMembers() { this.selectedMemberIds = this.activeMembers.map(m => m.id); }, deselectAllMembers() { this.selectedMemberIds = []; }, + handleMemberToggle({ memberId, checked }) { + this.selectedMemberIdForDialog = memberId; + if (checked && !this.selectedMemberIds.includes(memberId)) { + this.selectedMemberIds.push(memberId); + } else if (!checked) { + this.selectedMemberIds = this.selectedMemberIds.filter(id => id !== memberId); + } + }, + handleRecommendationToggle({ memberId, key, checked }) { + if (checked) { + if (!this.memberRecommendations[memberId]) { + this.$set ? this.$set(this.memberRecommendations, memberId, new Set()) : (this.memberRecommendations[memberId] = new Set()); + } + this.memberRecommendations[memberId].add(key); + } else { + if (this.memberRecommendations[memberId]) { + this.memberRecommendations[memberId].delete(key); + } + } + }, + getRecommendedKeys() { + if (!this.selectedMemberIdForDialog || !this.memberRecommendations[this.selectedMemberIdForDialog]) { + return []; + } + return Array.from(this.memberRecommendations[this.selectedMemberIdForDialog]); + }, splitDateTime(str) { if (!str) return { date: '–', time: '–' }; const s = String(str); @@ -1464,6 +1475,25 @@ th, td { border-bottom: 1px solid var(--border-color); padding: 0.5rem; text-ali font-weight: 700; color: #28a745; } + +/* Dialog-spezifische Styles */ +.member-dialog-controls { + display: flex; + gap: 0.5rem; + margin-bottom: 1rem; +} + +.member-dialog-layout { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 1.5rem; +} + +@media (max-width: 768px) { + .member-dialog-layout { + grid-template-columns: 1fr; + } +} diff --git a/frontend/src/views/ScheduleView.vue b/frontend/src/views/ScheduleView.vue index b3a733e..a4f77a0 100644 --- a/frontend/src/views/ScheduleView.vue +++ b/frontend/src/views/ScheduleView.vue @@ -74,17 +74,12 @@ - + + @@ -118,14 +113,18 @@ import MatchReportDialog from '../components/MatchReportDialog.vue'; import InfoDialog from '../components/InfoDialog.vue'; import ConfirmDialog from '../components/ConfirmDialog.vue'; +import BaseDialog from '../components/BaseDialog.vue'; +import CsvImportDialog from '../components/CsvImportDialog.vue'; export default { name: 'ScheduleView', components: { SeasonSelector, - MatchReportDialog - , + MatchReportDialog, InfoDialog, - ConfirmDialog}, + ConfirmDialog, + BaseDialog, + CsvImportDialog + }, computed: { ...mapGetters(['isAuthenticated', 'currentClub', 'clubs', 'currentClubName']), }, @@ -198,8 +197,9 @@ export default { this.showImportModal = false; this.selectedFile = null; }, - onFileSelected(event) { - this.selectedFile = event.target.files[0]; + handleCsvImport(file) { + this.selectedFile = file; + this.importCSV(); }, async importCSV() { if (!this.selectedFile) return; diff --git a/frontend/src/views/TrainingStatsView.vue b/frontend/src/views/TrainingStatsView.vue index c045e2f..65e94d7 100644 --- a/frontend/src/views/TrainingStatsView.vue +++ b/frontend/src/views/TrainingStatsView.vue @@ -113,52 +113,25 @@ - + +