Implement complete frontend with admin panel and theme system

- Add admin authentication and dashboard
- Create event management pages (list, create, edit, archive)
- Implement gallery enhancements (search, sorting, bulk download)
- Add email configuration and archive management pages
- Integrate Umami analytics with tracking throughout the app
- Add comprehensive error boundaries and loading states
- Implement accessibility features (WCAG 2.1 AA compliance)
- Create theme system with preset themes and customization
- Add branding settings and company information management
- Fix backend database initialization and health check
- Configure proper API URLs and environment variables

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-07-06 22:04:45 +02:00
parent 6c82958c79
commit 28632e8970
53 changed files with 13843 additions and 181 deletions
+45 -3
View File
@@ -1,11 +1,14 @@
import React, { useState } from 'react';
import { Download, Maximize2, Check } from 'lucide-react';
import { Download, Maximize2, Check, Package } from 'lucide-react';
import { useInView } from 'react-intersection-observer';
import { toast } from 'react-toastify';
import type { Photo } from '../../types';
import { useDownloadPhoto } from '../../hooks/useGallery';
import { PhotoLightbox } from './PhotoLightbox';
import { Button } from '../common';
import { galleryService } from '../../services/gallery.service';
import { analyticsService } from '../../services/analytics.service';
interface PhotoGridProps {
photos: Photo[];
@@ -34,6 +37,10 @@ export const PhotoGrid: React.FC<PhotoGridProps> = ({ photos, slug }) => {
const handleDownload = (photo: Photo, e: React.MouseEvent) => {
e.stopPropagation();
// Track individual photo download
analyticsService.trackDownload(photo.id, slug, false);
downloadPhotoMutation.mutate({
slug,
photoId: photo.id,
@@ -54,6 +61,40 @@ export const PhotoGrid: React.FC<PhotoGridProps> = ({ photos, slug }) => {
setSelectedPhotos(new Set());
};
const handleDownloadSelected = async () => {
if (selectedPhotos.size === 0) return;
const selectedPhotosList = photos.filter(p => selectedPhotos.has(p.id));
toast.info(`Downloading ${selectedPhotos.size} photos...`);
// Download each selected photo
const downloadPromises = selectedPhotosList.map(photo =>
galleryService.downloadPhoto(slug, photo.id, photo.filename)
.catch(err => {
console.error(`Failed to download ${photo.filename}:`, err);
return null;
})
);
try {
await Promise.all(downloadPromises);
toast.success(`Downloaded ${selectedPhotos.size} photos!`);
// Track bulk download
analyticsService.trackGalleryEvent('bulk_download', {
gallery: slug,
photo_count: selectedPhotos.size
});
// Clear selection after download
setSelectedPhotos(new Set());
setIsSelectionMode(false);
} catch (error) {
toast.error('Some photos failed to download');
}
};
if (photos.length === 0) {
return (
<div className="text-center py-12">
@@ -90,9 +131,10 @@ export const PhotoGrid: React.FC<PhotoGridProps> = ({ photos, slug }) => {
<Button
variant="primary"
size="sm"
leftIcon={<Download className="w-4 h-4" />}
leftIcon={<Package className="w-4 h-4" />}
onClick={handleDownloadSelected}
>
Download Selected
Download {selectedPhotos.size} Selected
</Button>
)}
</div>