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:
@@ -21,15 +21,36 @@ export const PhotoLightbox: React.FC<PhotoLightboxProps> = ({
|
||||
const [isDragging, setIsDragging] = useState(false);
|
||||
const [dragStart, setDragStart] = useState({ x: 0, y: 0 });
|
||||
const [dragOffset, setDragOffset] = useState({ x: 0, y: 0 });
|
||||
const [touchDistance, setTouchDistance] = useState<number | null>(null);
|
||||
|
||||
const downloadPhotoMutation = useDownloadPhoto();
|
||||
const currentPhoto = photos[currentIndex];
|
||||
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if (e.key === 'Escape') onClose();
|
||||
if (e.key === 'ArrowLeft') goToPrevious();
|
||||
if (e.key === 'ArrowRight') goToNext();
|
||||
switch (e.key) {
|
||||
case 'Escape':
|
||||
onClose();
|
||||
break;
|
||||
case 'ArrowLeft':
|
||||
goToPrevious();
|
||||
break;
|
||||
case 'ArrowRight':
|
||||
goToNext();
|
||||
break;
|
||||
case '+':
|
||||
case '=':
|
||||
handleZoomIn();
|
||||
break;
|
||||
case '-':
|
||||
case '_':
|
||||
handleZoomOut();
|
||||
break;
|
||||
case 'd':
|
||||
case 'D':
|
||||
handleDownload();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('keydown', handleKeyDown);
|
||||
@@ -102,6 +123,39 @@ export const PhotoLightbox: React.FC<PhotoLightboxProps> = ({
|
||||
}
|
||||
};
|
||||
|
||||
// Touch event handlers for pinch-to-zoom
|
||||
const handleTouchStart = (e: React.TouchEvent) => {
|
||||
if (e.touches.length === 2) {
|
||||
const touch1 = e.touches[0];
|
||||
const touch2 = e.touches[1];
|
||||
const distance = Math.hypot(
|
||||
touch2.clientX - touch1.clientX,
|
||||
touch2.clientY - touch1.clientY
|
||||
);
|
||||
setTouchDistance(distance);
|
||||
}
|
||||
};
|
||||
|
||||
const handleTouchMove = (e: React.TouchEvent) => {
|
||||
if (e.touches.length === 2 && touchDistance !== null) {
|
||||
const touch1 = e.touches[0];
|
||||
const touch2 = e.touches[1];
|
||||
const newDistance = Math.hypot(
|
||||
touch2.clientX - touch1.clientX,
|
||||
touch2.clientY - touch1.clientY
|
||||
);
|
||||
|
||||
const scale = newDistance / touchDistance;
|
||||
const newZoom = Math.max(1, Math.min(3, zoom * scale));
|
||||
setZoom(newZoom);
|
||||
setTouchDistance(newDistance);
|
||||
}
|
||||
};
|
||||
|
||||
const handleTouchEnd = () => {
|
||||
setTouchDistance(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 bg-black z-50 flex items-center justify-center">
|
||||
{/* Close button */}
|
||||
@@ -182,6 +236,9 @@ export const PhotoLightbox: React.FC<PhotoLightboxProps> = ({
|
||||
onMouseMove={handleMouseMove}
|
||||
onMouseUp={handleMouseUp}
|
||||
onMouseLeave={handleMouseUp}
|
||||
onTouchStart={handleTouchStart}
|
||||
onTouchMove={handleTouchMove}
|
||||
onTouchEnd={handleTouchEnd}
|
||||
style={{ cursor: zoom > 1 ? (isDragging ? 'grabbing' : 'grab') : 'default' }}
|
||||
>
|
||||
<img
|
||||
|
||||
Reference in New Issue
Block a user