diff --git a/backend/src/routes/adminFeedback.js b/backend/src/routes/adminFeedback.js index a8406cc..3cccf7d 100644 --- a/backend/src/routes/adminFeedback.js +++ b/backend/src/routes/adminFeedback.js @@ -60,8 +60,8 @@ router.put('/events/:eventId/feedback-settings', settings: updatedSettings }, eventId, { type: 'admin', - id: req.user.id, - name: req.user.username + id: req.admin.id, + name: req.admin.username }); res.json(updatedSettings); @@ -167,7 +167,7 @@ router.put('/feedback/:feedbackId/:action', return res.status(400).json({ error: 'Invalid action' }); } - await feedbackService.moderateFeedback(feedbackId, action, req.user.id); + await feedbackService.moderateFeedback(feedbackId, action, req.admin.id); res.json({ success: true }); } catch (error) { @@ -184,7 +184,7 @@ router.delete('/feedback/:feedbackId', try { const { feedbackId } = req.params; - await feedbackService.deleteFeedback(feedbackId, req.user.id); + await feedbackService.deleteFeedback(feedbackId, req.admin.id); res.json({ success: true }); } catch (error) { diff --git a/frontend/nginx.conf b/frontend/nginx.conf index 2e31dd3..0d03b3d 100644 --- a/frontend/nginx.conf +++ b/frontend/nginx.conf @@ -4,6 +4,10 @@ server { root /usr/share/nginx/html; index index.html; + # Allow larger file uploads (up to 100MB) + client_max_body_size 100M; + client_body_timeout 300s; + # Gzip compression gzip on; gzip_vary on; @@ -49,6 +53,10 @@ server { proxy_set_header X-Forwarded-Proto $scheme; proxy_cache_bypass $http_upgrade; proxy_read_timeout 86400; + + # Allow larger uploads for API endpoints + client_max_body_size 100M; + client_body_timeout 300s; } # Photo serving proxy diff --git a/frontend/src/components/admin/FeedbackModerationPanel.tsx b/frontend/src/components/admin/FeedbackModerationPanel.tsx index 18c15ab..34dbb03 100644 --- a/frontend/src/components/admin/FeedbackModerationPanel.tsx +++ b/frontend/src/components/admin/FeedbackModerationPanel.tsx @@ -118,7 +118,12 @@ export const FeedbackModerationPanel: React.FC = ( - {format(parseISO(item.created_at), 'MMM d, h:mm a')} + {format( + typeof item.created_at === 'string' + ? parseISO(item.created_at) + : new Date(item.created_at), + 'MMM d, h:mm a' + )}

{item.comment}

diff --git a/frontend/src/config/api.ts b/frontend/src/config/api.ts index 14bd410..65ed3dc 100644 --- a/frontend/src/config/api.ts +++ b/frontend/src/config/api.ts @@ -48,10 +48,25 @@ api.interceptors.request.use( const galleryMatch = config.url?.match(/gallery\/([^\/]+)/); if (galleryMatch && galleryMatch[1]) { - const gallerySlug = galleryMatch[1]; + const galleryIdOrSlug = galleryMatch[1]; // Remove any query parameters from the slug - const cleanSlug = gallerySlug.split('?')[0]; - const token = localStorage.getItem(`gallery_token_${cleanSlug}`); + const cleanIdOrSlug = galleryIdOrSlug.split('?')[0]; + + // Check if it's a numeric ID (for upload endpoints) + let token = null; + if (/^\d+$/.test(cleanIdOrSlug)) { + // It's an event ID - try to find the token from current page slug + const pathParts = window.location.pathname.split('/'); + if (pathParts[1] === 'gallery' && pathParts[2]) { + const gallerySlug = pathParts[2]; + const cleanSlug = gallerySlug.split('?')[0]; + token = localStorage.getItem(`gallery_token_${cleanSlug}`); + } + } else { + // It's a slug - use it directly + token = localStorage.getItem(`gallery_token_${cleanIdOrSlug}`); + } + if (token) { if (!config.headers) { config.headers = {}; diff --git a/frontend/src/i18n/locales/de.json b/frontend/src/i18n/locales/de.json index b7e75e0..1a47adf 100644 --- a/frontend/src/i18n/locales/de.json +++ b/frontend/src/i18n/locales/de.json @@ -134,7 +134,6 @@ "sortByName": "Nach Name sortieren", "sortBySize": "Nach Größe sortieren", "allPhotos": "Alle Fotos", - "downloadSelected": "Ausgewählte herunterladen", "shareGallery": "Galerie teilen", "needHelp": "Hilfe benötigt? Kontaktieren Sie uns unter", "noPhotosFound": "Keine Fotos gefunden", diff --git a/frontend/src/pages/admin/EventDetailsPage.tsx b/frontend/src/pages/admin/EventDetailsPage.tsx index 784299c..ab3c2ba 100644 --- a/frontend/src/pages/admin/EventDetailsPage.tsx +++ b/frontend/src/pages/admin/EventDetailsPage.tsx @@ -279,12 +279,38 @@ export const EventDetailsPage: React.FC = () => { const handleCopyLink = async () => { try { - await navigator.clipboard.writeText(event.share_link); + // Check if share_link exists + if (!event.share_link) { + toast.error(t('errors.noShareLink', 'No share link available')); + return; + } + + // Try modern clipboard API first + if (navigator.clipboard && window.isSecureContext) { + await navigator.clipboard.writeText(event.share_link); + } else { + // Fallback for non-HTTPS contexts or older browsers + const textArea = document.createElement('textarea'); + textArea.value = event.share_link; + textArea.style.position = 'fixed'; + textArea.style.left = '-999999px'; + textArea.style.top = '-999999px'; + document.body.appendChild(textArea); + textArea.focus(); + textArea.select(); + const successful = document.execCommand('copy'); + document.body.removeChild(textArea); + if (!successful) { + throw new Error('Copy failed'); + } + } + setCopiedLink(true); setTimeout(() => setCopiedLink(false), 2000); toast.success(t('toast.linkCopied')); } catch (err) { - toast.error(t('errors.somethingWentWrong')); + console.error('Copy failed:', err); + toast.error(t('errors.copyFailed', 'Failed to copy link. Please copy manually.')); } };