From b166f7c7d5b39e5cea86ae728f3316d63327ea25 Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Wed, 12 Nov 2025 12:53:29 +0100 Subject: [PATCH] Refactor member gallery dialog in DiaryView for improved functionality and user experience This commit replaces the existing BaseDialog for the member gallery with a new MemberGalleryDialog component, streamlining the dialog's functionality. The new component integrates props for current club, date, and participant status, enhancing interactivity. Additionally, redundant gallery loading logic and state management have been removed, simplifying the codebase and improving maintainability. --- .../src/components/MemberGalleryDialog.vue | 356 ++++++++++++++++++ frontend/src/views/DiaryView.vue | 320 +--------------- 2 files changed, 366 insertions(+), 310 deletions(-) create mode 100644 frontend/src/components/MemberGalleryDialog.vue diff --git a/frontend/src/components/MemberGalleryDialog.vue b/frontend/src/components/MemberGalleryDialog.vue new file mode 100644 index 0000000..3b2c0c0 --- /dev/null +++ b/frontend/src/components/MemberGalleryDialog.vue @@ -0,0 +1,356 @@ + + + + + + diff --git a/frontend/src/views/DiaryView.vue b/frontend/src/views/DiaryView.vue index a254c63..3df6adf 100644 --- a/frontend/src/views/DiaryView.vue +++ b/frontend/src/views/DiaryView.vue @@ -502,49 +502,13 @@ /> - - - + :current-club="currentClub" + :date="date" + :is-participant="isParticipant" + @member-click="handleGalleryMemberClick" + /> @@ -585,6 +549,7 @@ import TagHistoryDialog from '../components/TagHistoryDialog.vue'; import MemberActivityStatsDialog from '../components/MemberActivityStatsDialog.vue'; import AccidentFormDialog from '../components/AccidentFormDialog.vue'; import QuickAddMemberDialog from '../components/QuickAddMemberDialog.vue'; +import MemberGalleryDialog from '../components/MemberGalleryDialog.vue'; export default { name: 'DiaryView', @@ -600,7 +565,8 @@ export default { TagHistoryDialog, MemberActivityStatsDialog, AccidentFormDialog, - QuickAddMemberDialog + QuickAddMemberDialog, + MemberGalleryDialog }, data() { return { @@ -690,11 +656,6 @@ export default { editingActivityId: null, // ID der Aktivität, die gerade bearbeitet wird // Suche für Inline-Edit showGalleryDialog: false, - galleryLoading: false, - galleryImageUrl: null, - galleryError: '', - gallerySize: 200, - galleryMembers: [], editShowDropdown: false, editSearchResults: [], editSearchForId: null, @@ -807,104 +768,11 @@ export default { } this.confirmDialog.isOpen = false; }, - revokeGalleryImage() { - if (this.galleryImageUrl) { - URL.revokeObjectURL(this.galleryImageUrl); - this.galleryImageUrl = null; - } - }, async openGalleryDialog() { - if (!this.currentClub || this.galleryLoading) { + if (!this.currentClub) { return; } this.showGalleryDialog = true; - await this.loadGalleryMembers(); - }, - updateGallerySizeBasedOnCount() { - const count = this.galleryMembers.length; - if (count < 11) { - this.gallerySize = 200; - } else if (count < 22) { - this.gallerySize = 150; - } else { - this.gallerySize = 100; - } - }, - async loadGalleryMembers() { - if (!this.currentClub || this.galleryLoading) { - return; - } - this.galleryLoading = true; - this.galleryError = ''; - this.galleryImageUrl = null; - this.revokeGalleryImage(); - try { - const response = await apiClient.get(`/clubmembers/gallery/${this.currentClub}?format=json&size=${this.gallerySize}`); - const members = response.data.members || []; - - // Setze Größe basierend auf Anzahl der Mitglieder (vor dem Laden der Bilder) - this.galleryMembers = members; // Temporär setzen für count - this.updateGallerySizeBasedOnCount(); - - // Lade Bilder als Blobs und erstelle ObjectURLs - this.galleryMembers = await Promise.all(members.map(async (member) => { - try { - const imageUrl = await this.loadMemberImageAsBlob(member.memberId); - console.log(`[loadGalleryMembers] Member ${member.memberId} (${member.fullName}): imageUrl =`, imageUrl ? 'OK' : 'null'); - return { - ...member, - imageUrl: imageUrl || null - }; - } catch (error) { - console.error(`[loadGalleryMembers] Fehler beim Laden des Bildes für Mitglied ${member.memberId}:`, error); - return { - ...member, - imageUrl: null - }; - } - })); - console.log('[loadGalleryMembers] Geladene Mitglieder:', this.galleryMembers.map(m => ({ id: m.memberId, name: m.fullName, hasImage: !!m.imageUrl }))); - } catch (error) { - console.error('Fehler beim Laden der Galerie:', error); - this.galleryError = error?.response?.data?.error || 'Galerie konnte nicht geladen werden.'; - this.galleryMembers = []; - } finally { - this.galleryLoading = false; - } - }, - async loadMemberImageAsBlob(memberId) { - try { - const response = await apiClient.get(`/clubmembers/image/${this.currentClub}/${memberId}`, { responseType: 'blob' }); - if (!response.data || response.data.size === 0) { - console.warn(`[loadMemberImageAsBlob] Leeres Blob für Mitglied ${memberId}`); - return null; - } - const objectUrl = URL.createObjectURL(response.data); - console.log(`[loadMemberImageAsBlob] ObjectURL erstellt für Mitglied ${memberId}:`, objectUrl.substring(0, 50) + '...'); - return objectUrl; - } catch (error) { - // 404 ist OK - Mitglied hat kein Bild - if (error?.response?.status === 404) { - console.log(`[loadMemberImageAsBlob] Kein Bild für Mitglied ${memberId} (404)`); - return null; - } - console.error(`[loadMemberImageAsBlob] Fehler beim Laden des Bildes für Mitglied ${memberId}:`, error); - return null; - } - }, - async regenerateGallery() { - await this.loadGalleryMembers(); - }, - closeGalleryDialog() { - this.showGalleryDialog = false; - this.revokeGalleryImage(); - // Revoke all object URLs - this.galleryMembers.forEach(member => { - if (member.imageUrl) { - URL.revokeObjectURL(member.imageUrl); - } - }); - this.galleryMembers = []; }, async handleGalleryMemberClick(member) { if (!this.date || this.date === 'new') { @@ -913,14 +781,6 @@ export default { console.log('[handleGalleryMemberClick] Clicked member:', member); await this.toggleParticipant(member.memberId); }, - handleImageError(member) { - console.error(`[handleImageError] Bild konnte nicht geladen werden für Mitglied ${member.memberId} (${member.fullName}), URL:`, member.imageUrl); - // Setze imageUrl auf null, damit der Placeholder angezeigt wird - member.imageUrl = null; - }, - handleImageLoad(member) { - console.log(`[handleImageLoad] Bild erfolgreich geladen für Mitglied ${member.memberId} (${member.fullName})`); - }, hasActivityVisual(pa) { if (!pa) return false; @@ -2527,7 +2387,6 @@ export default { if (this.timeChecker) { clearInterval(this.timeChecker); } - this.revokeGalleryImage(); } }; @@ -2585,165 +2444,6 @@ h3 { align-self: flex-end; } -.gallery-dialog-content { - display: flex; - flex-direction: column; - justify-content: flex-start; - align-items: flex-start; - padding: 0; - min-height: 60vh; - max-height: 70vh; - overflow: auto; -} - -.gallery-controls { - width: 100%; - padding: 12px 16px; - border-bottom: 1px solid var(--border-color, #ddd); - display: flex; - align-items: center; - gap: 12px; - background: #f9f9f9; - flex-shrink: 0; - position: relative; - z-index: 10; -} - -.gallery-controls label { - font-weight: 500; - color: var(--text-color, #333); -} - -.gallery-controls select { - padding: 6px 12px; - border: 1px solid var(--border-color, #ddd); - border-radius: 4px; - font-size: 14px; - background: white; - cursor: pointer; -} - -.gallery-controls select:disabled { - opacity: 0.6; - cursor: not-allowed; -} - -.gallery-image-wrapper { - width: 100%; - display: flex; - justify-content: flex-start; - align-items: flex-start; -} - -.gallery-dialog-image { - display: block; - max-width: 100%; - width: auto; - height: auto; - border-radius: 8px; - box-shadow: 0 4px 18px rgba(0, 0, 0, 0.35); - position: static !important; - left: auto !important; - top: auto !important; - border: none !important; -} - -.gallery-loading, -.gallery-error { - font-size: 1rem; - color: var(--text-color, #333); -} - -.gallery-members-grid { - display: grid; - grid-template-columns: repeat(auto-fill, max-content); - gap: 0; - padding: 0; - width: 100%; - position: relative; - z-index: 1; - align-items: start; - box-sizing: border-box; - justify-items: start; -} - -.gallery-member-item { - display: block; - cursor: pointer; - padding: 0; - border: none; - border-radius: 0; - transition: all 0.2s ease; - margin: 0; - box-sizing: border-box; - overflow: hidden; - position: relative; -} - -.gallery-member-item:hover { - background-color: transparent; -} - -.gallery-member-item.is-participant { - background-color: transparent; - border: none; -} - -.gallery-member-image { - object-fit: cover; - border-radius: 0; - margin: 0; - pointer-events: none; /* Prevent image from blocking clicks on parent */ - display: block; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - z-index: 0; - max-width: 100%; - max-height: 100%; - border: none !important; -} - -.gallery-member-name { - font-size: 12px; - text-align: center; - color: #ff6b6b; - font-weight: 500; - margin: 0; - padding: 4px 8px; - position: absolute; - bottom: 0; - left: 0; - right: 0; - width: 100%; - box-sizing: border-box; - background-color: rgba(0, 0, 0, 0.5) !important; - z-index: 10; -} - -.gallery-member-item.is-participant .gallery-member-name { - color: #51cf66; -} - -.gallery-member-placeholder { - display: flex; - align-items: center; - justify-content: center; - background-color: #f0f0f0; - border-radius: 0; - color: #999; - font-size: 12px; - margin: 0; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - box-sizing: border-box; -} - .column:first-child { flex: 1; overflow: visible;