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;