Untitled

 avatar
unknown
plain_text
13 days ago
37 kB
5
Indexable
"use client"

import { useState, useEffect, useRef, createRef } from "react"
import { Download, MessageCircle, ZoomIn, ZoomOut, ChevronLeft, ChevronRight, X, Heart, Check } from "lucide-react"
import { Button } from "@/components/ui/button"
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from "@/components/ui/dialog"
import { PhotoComments } from "./photo-comments"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { Slider } from "@/components/ui/slider"
import { toast } from "sonner"
import { ScrollToolbar } from "./ScrollToolbar"
import { useInView } from "framer-motion"

interface Section {
  title: string;
  description?: string;
  images: {
    src: string;
    alt: string;
    width: number;
    height: number;
    thumbnailSrc?: string;
  }[];
}

interface GalleryProps {
  sections: Section[];
  clientConfig: any;
  isSelectionView?: boolean;
}

interface CommentCounts {
  [key: string]: number;
}

interface ImageDialogProps {
  sections: Section[];
  currentSectionIndex: number;
  currentImageIndex: number;
  commentCounts: CommentCounts;
  selectedTab: string;
  setSelectedTab: (tab: string) => void;
  isFullImageLoaded: boolean;
  setIsFullImageLoaded: (loaded: boolean) => void;
  handleDownload: (src: string) => Promise<void>;
  isDownloading: boolean;
  onCommentAdded: (photoId: string) => void;
  onNavigate: (direction: 'prev' | 'next') => void;
  onClose: () => void;
  favorites: {[key: string]: boolean};
  toggleFavorite: (photoId: string, e: React.MouseEvent) => void;
}

function ImageDialog({
  sections,
  currentSectionIndex,
  currentImageIndex,
  commentCounts,
  selectedTab,
  setSelectedTab,
  isFullImageLoaded,
  setIsFullImageLoaded,
  handleDownload,
  isDownloading,
  onCommentAdded,
  onNavigate,
  onClose,
  favorites,
  toggleFavorite
}: ImageDialogProps) {
  const section = sections[currentSectionIndex];
  const image = section.images[currentImageIndex];
  const fullSizeImageSrc = image.src;
  const thumbnailSrc = image.thumbnailSrc;
  const photoId = `${section.title}-${currentImageIndex}`;
  const commentCount = commentCounts[photoId] || 0;
  const isFavorite = favorites[photoId] || false;

  const hasPrevious = currentImageIndex > 0;
  const hasNext = currentImageIndex < section.images.length - 1;

  // Function to handle image click and open in new tab
  const handleImageClick = () => {
    if (isFullImageLoaded) {
      window.open(fullSizeImageSrc, '_blank');
    }
  };

  return (
    <div className="flex flex-col h-full">
      {/* Custom close button */}
      <button 
        onClick={onClose}
        className="absolute right-2 top-2 z-50 p-1 rounded-full bg-background/80 backdrop-blur-sm border border-border hover:bg-muted transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-primary"
        aria-label="Close dialog"
      >
        <X className="h-3.5 w-3.5" />
      </button>
      
      <Tabs value={selectedTab} onValueChange={setSelectedTab} className="flex flex-col h-full">
        <div className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-2 mb-4">
          <TabsList className="h-8 sm:h-9 p-1 w-auto">
            <TabsTrigger 
              value="preview" 
              className="text-xs sm:text-sm px-2 py-1 sm:px-3 h-6 sm:h-7 data-[state=active]:shadow-sm rounded-md mx-0.5"
            >
              Vorschau
            </TabsTrigger>
            <TabsTrigger 
              value="comments" 
              className="text-xs sm:text-sm px-2 py-1 sm:px-3 h-6 sm:h-7 data-[state=active]:shadow-sm rounded-md mx-0.5"
            >
              Kommentare
              {commentCount > 0 && (
                <span className="ml-1 bg-primary/10 text-primary rounded-full px-1 py-0.5 text-xs">
                  {commentCount}
                </span>
              )}
            </TabsTrigger>
          </TabsList>
          
          <div className="flex items-center gap-2">
            <Button 
              variant="ghost"
              size="sm"
              className={`h-8 sm:h-9 aspect-square p-0 ${isFavorite ? 'text-pink-500' : 'text-muted-foreground hover:text-white'}`}
              onClick={(e) => toggleFavorite(photoId, e)}
            >
              <Heart 
                className="h-5 w-5 transition-transform duration-200 ease-out" 
                fill={isFavorite ? 'currentColor' : 'none'} 
                style={{ transform: isFavorite ? 'scale(1.1)' : 'scale(1)' }}
              />
            </Button>
            
            <Button 
              onClick={() => handleDownload(fullSizeImageSrc)}
              disabled={isDownloading}
              size="sm"
              className="h-8 sm:h-9 text-sm"
            >
              <Download className="mr-2 h-4 w-4" />
              Bild herunterladen
            </Button>
          </div>
        </div>

        <div className="flex-1 min-h-0 relative rounded-lg bg-background">
          <TabsContent 
            value="preview" 
            className="h-full"
          >
            <div className="h-full flex items-center justify-center p-4">
              {/* Previous button */}
              {hasPrevious && (
                <button 
                  onClick={(e) => {
                    e.stopPropagation();
                    onNavigate('prev');
                  }}
                  className="absolute left-4 top-1/2 -translate-y-1/2 z-20 flex items-center justify-center w-10 h-10 bg-black/40 hover:bg-black/60 text-white rounded-full transition-all duration-150"
                  aria-label="Previous image"
                >
                  <ChevronLeft className="h-6 w-6" />
                </button>
              )}

              {/* Next button */}
              {hasNext && (
                <button 
                  onClick={(e) => {
                    e.stopPropagation();
                    onNavigate('next');
                  }}
                  className="absolute right-4 top-1/2 -translate-y-1/2 z-20 flex items-center justify-center w-10 h-10 bg-black/40 hover:bg-black/60 text-white rounded-full transition-all duration-150"
                  aria-label="Next image"
                >
                  <ChevronRight className="h-6 w-6" />
                </button>
              )}

              {/* Images */}
              <img
                key={`thumb-${photoId}`}
                src={thumbnailSrc}
                alt={image.alt}
                className="max-w-full max-h-full w-auto h-auto object-contain transition-opacity duration-300"
                style={{ opacity: isFullImageLoaded ? 0 : 1 }}
              />
              <img
                key={`full-${photoId}`}
                src={fullSizeImageSrc}
                alt={image.alt}
                className="max-w-full max-h-full w-auto h-auto object-contain transition-opacity duration-300 absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2"
                style={{ 
                  opacity: isFullImageLoaded ? 1 : 0,
                  cursor: isFullImageLoaded ? "zoom-in" : "default" 
                }}
                onLoad={() => setIsFullImageLoaded(true)}
                onClick={handleImageClick}
                title={isFullImageLoaded ? "Klicken, um in voller Größe zu öffnen" : ""}
              />
            </div>
          </TabsContent>
          
          <TabsContent 
            value="comments" 
            className="absolute inset-0 overflow-y-auto"
          >
            <div className="p-4 w-full">
              <style jsx global>{`
                .rounded-lg.border.bg-card {
                  max-width: 100%;
                  overflow: hidden;
                }
                .rounded-lg.border.bg-card > div {
                  width: 100%;
                }
                .rounded-lg.border.bg-card > div > div {
                  flex-wrap: wrap;
                  gap: 0.5rem;
                }
                .rounded-lg.border.bg-card > div > div > span {
                  order: 2;
                  width: 100%;
                  text-align: left;
                  padding-left: 3.25rem;
                }
              `}</style>
              <PhotoComments 
                key={`comments-${photoId}`}
                photoId={photoId}
                onCommentAdded={() => onCommentAdded(photoId)}
              />

              {/* Navigation in comments view too */}
              <div className="flex justify-between items-center mt-4">
                {hasPrevious ? (
                  <Button
                    variant="outline"
                    size="sm"
                    onClick={() => onNavigate('prev')}
                    className="flex items-center gap-1"
                  >
                    <ChevronLeft className="h-4 w-4" />
                    Vorheriges Bild
                  </Button>
                ) : <div />}
                
                {hasNext && (
                  <Button
                    variant="outline"
                    size="sm"
                    onClick={() => onNavigate('next')}
                    className="flex items-center gap-1 ml-auto"
                  >
                    Nächstes Bild
                    <ChevronRight className="h-4 w-4" />
                  </Button>
                )}
              </div>
            </div>
          </TabsContent>
        </div>

        {/* Navigation and counter indicator at the bottom */}
        <div className="flex items-center justify-center gap-2 mt-4">
          <span className="text-sm text-muted-foreground">
            {currentImageIndex + 1} / {section.images.length}
          </span>
        </div>
      </Tabs>
    </div>
  );
}

export function Gallery({ sections, clientConfig, isSelectionView = false }: GalleryProps) {
  const [currentSectionIndex, setCurrentSectionIndex] = useState<number>(0);
  const [currentImageIndex, setCurrentImageIndex] = useState<number>(0);
  const [isDownloading, setIsDownloading] = useState(false);
  const [isFullImageLoaded, setIsFullImageLoaded] = useState(false);
  const [selectedTab, setSelectedTab] = useState<string>("preview");
  const [isDialogOpen, setIsDialogOpen] = useState(false);
  const [commentCounts, setCommentCounts] = useState<{[key: string]: number}>({});
  const defaultGridSize = clientConfig.gallery?.defaultGridSize || 3;
  const [gridSize, setGridSize] = useState<number>(defaultGridSize);

  // Use client config for rounded images or fallback to true
  const roundedImages = clientConfig.gallery?.roundedImages !== undefined 
    ? clientConfig.gallery.roundedImages 
    : true;

  const [favorites, setFavorites] = useState<{[key: string]: boolean}>({});
  const [showFavoritesOnly, setShowFavoritesOnly] = useState<boolean>(false);
  const [showCommentsOnly, setShowCommentsOnly] = useState<boolean>(false);
  
  // Create a refs object to store references to each image element
  const imageRefs = useRef<{[key: string]: React.RefObject<HTMLDivElement>}>({});
  
  // For scroll toolbar
  const footerRef = useRef(null);
  const inView = useInView(footerRef, { amount: 0.1 });

  // Single reference to the currently open Dialog
  const [activeDialogId, setActiveDialogId] = useState<string | null>(null);

  // Initialize refs for all images
  useEffect(() => {
    // Create refs for all images across all sections
    sections.forEach((section) => {
      section.images.forEach((_, imageIndex) => {
        const photoId = `${section.title}-${imageIndex}`;
        if (!imageRefs.current[photoId]) {
          imageRefs.current[photoId] = createRef();
        }
      });
    });
  }, [sections]);

  // Function to scroll to the current image in the gallery
  const scrollToCurrentImage = () => {
    const photoId = `${sections[currentSectionIndex].title}-${currentImageIndex}`;
    const ref = imageRefs.current[photoId];
    
    if (ref && ref.current) {
      // Use a small timeout to ensure DOM has updated
      setTimeout(() => {
        ref.current?.scrollIntoView({
          behavior: 'smooth',
          block: 'center'
        });
      }, 100);
    }
  };

  // Load favorites from localStorage on initial render
  useEffect(() => {
    const storedFavorites = localStorage.getItem('favorites');
    if (storedFavorites) {
      try {
        setFavorites(JSON.parse(storedFavorites));
      } catch (e) {
        console.error('Failed to parse favorites from localStorage:', e);
      }
    }
  }, []);

  // Add special behavior for selection view to auto-favorite images
  useEffect(() => {
    if (isSelectionView && sections.length > 0) {
      // Create a new favorites object that includes all images in the selection
      const selectionFavorites = { ...favorites };
      
      // Mark all images in the selection as favorites
      sections.forEach((section) => {
        section.images.forEach((_, imageIndex) => {
          const photoId = `${section.title}-${imageIndex}`;
          // Only set if not already set (don't override user choices)
          if (selectionFavorites[photoId] === undefined) {
            selectionFavorites[photoId] = true;
          }
        });
      });
      
      // Update favorites state
      setFavorites(selectionFavorites);

      // We don't save to localStorage when in selection view
      // to avoid polluting main gallery favorites
    }
  }, [isSelectionView, sections]);

  // Function to toggle favorites
  const toggleFavorite = (photoId: string, e: React.MouseEvent) => {
    e.stopPropagation(); // Prevent opening the image dialog
    const newFavorites = { ...favorites };
    newFavorites[photoId] = !newFavorites[photoId];
    setFavorites(newFavorites);
    localStorage.setItem('favorites', JSON.stringify(newFavorites));
  };

  // Function to reset all favorites
  const handleResetAllFavorites = () => {
    // Clear all favorites
    setFavorites({});
    
    // Clear from localStorage
    localStorage.removeItem('favorites');
    
    // Show confirmation toast
    toast.success('Alle Favoriten wurden zurückgesetzt');
    
    // If currently showing favorites only, turn off this filter
    if (showFavoritesOnly) {
      setShowFavoritesOnly(false);
    }
  };

  // Function to create a shared link with favorites
  const handleCreateSharedLink = async (password: string, requirePassword: boolean) => {
    // First, find which selection number to use
    const clientId = window.location.pathname.split('/')[1];
    
    // Get all favorited photos
    const selectedFavorites: any = {};
    
    sections.forEach((section) => {
      section.images.forEach((image, imageIndex) => {
        const photoId = `${section.title}-${imageIndex}`;
        if (favorites[photoId]) {
          if (!selectedFavorites[section.title]) {
            selectedFavorites[section.title] = [];
          }
          selectedFavorites[section.title].push(imageIndex);
        }
      });
    });
    
    // Create selection data
    const selectionData = {
      favorites: selectedFavorites,
      requirePassword,
      password: requirePassword ? password : "",
      createdAt: new Date().toISOString(),
      parentClientId: clientId
    };
    
    try {
      // Determine the next available selection ID
      let nextSelectionId = 1;
      
      try {
        const response = await fetch(`/api/selections?clientId=${encodeURIComponent(clientId)}`);
        if (response.ok) {
          const selections = await response.json();
          if (selections && selections.length > 0) {
            // Extract all numeric selection IDs
            const numericIds = selections
              .map(s => parseInt(s.selection_id))
              .filter(id => !isNaN(id));
            
            if (numericIds.length > 0) {
              // Find the highest ID and increment
              nextSelectionId = Math.max(...numericIds) + 1;
            }
          }
        }
      } catch (error) {
        console.error("Error fetching existing selections:", error);
        // Continue with the default ID 1 if there's an error
      }
      
      // Save the selection to the server
      const response = await fetch(`/api/selections`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          clientId,
          selectionId: nextSelectionId.toString(),
          data: selectionData
        }),
      });
      
      if (!response.ok) {
        throw new Error("Failed to create selection");
      }
      
      // Return the shareable URL
      const origin = window.location.origin;
      const shareableUrl = `${origin}/${clientId}/auswahl/${nextSelectionId}`;
      
      // If a password is set, include it in the hash
      const fullUrl = requirePassword ? `${shareableUrl}#password=${password}` : shareableUrl;
      
      return fullUrl;
    } catch (error) {
      console.error("Error creating shared link:", error);
      throw error;
    }
  };

  // Reset state when dialog closes and scroll to current image
  useEffect(() => {
    if (!isDialogOpen) {
      setSelectedTab("preview");
      setIsFullImageLoaded(false);
      setActiveDialogId(null);
      
      // Scroll to the current image when the dialog closes
      // Small delay to ensure DOM is ready
      setTimeout(scrollToCurrentImage, 200);
    }
  }, [isDialogOpen]);

  // Fetch comment counts when component mounts
  useEffect(() => {
    const fetchCommentCounts = async () => {
      const clientId = window.location.pathname.split('/')[1];
      
      try {
        const response = await fetch(`/api/comments?clientId=${encodeURIComponent(clientId)}&index=true`);
        if (response.ok) {
          const counts = await response.json();
          setCommentCounts(counts);
        }
      } catch (error) {
        console.error('Error fetching comment counts:', error);
      }
    };
  
    fetchCommentCounts();
  }, []);

  // Reset full image loading state when image changes
  useEffect(() => {
    setIsFullImageLoaded(false);
  }, [currentImageIndex]);

  // Handle downloading all favorites
  const handleDownloadAllFavorites = async () => {
    const favoritePhotos = [];
    
    // Collect all favorited photos
    sections.forEach((section) => {
      section.images.forEach((image, imageIndex) => {
        const photoId = `${section.title}-${imageIndex}`;
        if (favorites[photoId]) {
          favoritePhotos.push({
            src: image.src,
            filename: image.src.split('/').pop() || `favorite-${photoId}.jpg`
          });
        }
      });
    });
    
    if (favoritePhotos.length === 0) {
      toast.error("Keine Favoriten zum Herunterladen gefunden");
      return;
    }
    
    // Show initial toast
    toast.info(`Download von ${favoritePhotos.length} Favoriten wird gestartet...`);
    
    let downloadedCount = 0;
    let failedCount = 0;
    
    // Download files one by one with small delay to prevent browser throttling
    for (const photo of favoritePhotos) {
      try {
        await new Promise(resolve => setTimeout(resolve, 300)); // Small delay between downloads
        
        const response = await fetch(photo.src, {
          method: 'GET',
          headers: {
            'Cache-Control': 'no-cache'
          }
        });
        
        if (!response.ok) {
          throw new Error(`HTTP error! status: ${response.status}`);
        }
        
        const blob = await response.blob();
        if (blob.size === 0) {
          throw new Error('Downloaded file is empty');
        }
        
        const url = window.URL.createObjectURL(blob);
        const link = document.createElement('a');
        link.href = url;
        link.download = photo.filename;
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        window.URL.revokeObjectURL(url);
        
        downloadedCount++;
        
        // Update progress every 3 files or for the last one
        if (downloadedCount % 3 === 0 || downloadedCount === favoritePhotos.length) {
          toast.info(`${downloadedCount} von ${favoritePhotos.length} Favoriten heruntergeladen...`);
        }
        
      } catch (error) {
        console.error(`Failed to download ${photo.filename}:`, error);
        failedCount++;
      }
    }
    
    // Show final toast
    if (failedCount > 0) {
      toast.warning(`${downloadedCount} von ${favoritePhotos.length} Favoriten heruntergeladen, ${failedCount} fehlgeschlagen.`);
    } else {
      toast.success(`Alle ${favoritePhotos.length} Favoriten erfolgreich heruntergeladen!`);
    }
  };

  const handleDownload = async (imageSrc: string) => {
    try {
      setIsDownloading(true);
      
      const fileName = imageSrc.split('/').pop() || 'image';
      const response = await fetch(imageSrc, {
        method: 'GET',
        headers: {
          'Cache-Control': 'no-cache'
        }
      });
      
      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }
      
      const contentType = response.headers.get('content-type');
      if (!contentType?.includes('image')) {
        throw new Error(`Invalid content type: ${contentType}`);
      }
  
      const blob = await response.blob();
      if (blob.size === 0) {
        throw new Error('Downloaded file is empty');
      }
  
      const url = window.URL.createObjectURL(blob);
      const link = document.createElement('a');
      link.href = url;
      link.download = fileName;
      document.body.appendChild(link);
      link.click();
      document.body.removeChild(link);
      window.URL.revokeObjectURL(url);
      
      toast.success('Download erfolgreich');
    } catch (error) {
      console.error('Download error:', error);
      toast.error(`Download fehlgeschlagen: ${error.message}`);
    } finally {
      setIsDownloading(false);
    }
  };

  // Handle image navigation with scrolling to keep the current image visible
  const handleNavigate = (direction: 'prev' | 'next') => {
    if (direction === 'prev' && currentImageIndex > 0) {
      setCurrentImageIndex(currentImageIndex - 1);
      // Scroll to the new current image after state update
      setTimeout(scrollToCurrentImage, 100);
    } else if (direction === 'next' && currentImageIndex < sections[currentSectionIndex].images.length - 1) {
      setCurrentImageIndex(currentImageIndex + 1);
      // Scroll to the new current image after state update
      setTimeout(scrollToCurrentImage, 100);
    }
  };

  // Handle keyboard navigation
  useEffect(() => {
    const handleKeyDown = (e: KeyboardEvent) => {
      if (!isDialogOpen) return;
      
      if (e.key === 'ArrowLeft') {
        handleNavigate('prev');
      } else if (e.key === 'ArrowRight') {
        handleNavigate('next');
      } else if (e.key === 'Escape') {
        setIsDialogOpen(false);
      }
    };
    
    window.addEventListener('keydown', handleKeyDown);
    return () => window.removeEventListener('keydown', handleKeyDown);
  }, [isDialogOpen, currentImageIndex, currentSectionIndex]);

  // Helper function to determine image type
  const getImageType = (image) => {
    const aspectRatio = image.width / image.height;
    if (aspectRatio < 0.8) return 'portrait';
    if (aspectRatio > 1.3) return 'landscape';
    return 'square';
  };

  // CSS classes for the grid based on the slider value
  const getGridClasses = () => {
    // gridSize ranges from 1 to 6
    // Lower values = bigger thumbnails, fewer per row
    // Higher values = smaller thumbnails, more per row
    const sizeMap = {
      1: 'grid-cols-1 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-2 xl:grid-cols-2',
      2: 'grid-cols-1 sm:grid-cols-2 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-3',
      3: 'grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-4',
      4: 'grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6',
      5: 'grid-cols-2 sm:grid-cols-3 md:grid-cols-5 lg:grid-cols-6 xl:grid-cols-7',
      6: 'grid-cols-2 sm:grid-cols-4 md:grid-cols-6 lg:grid-cols-8 xl:grid-cols-8',
    };
    
    return sizeMap[gridSize] || sizeMap[3]; // Default to 3 if invalid
  };

  return (
    <div className="space-y-12">
      {/* Main Dialog (single instance for all images) */}
      <Dialog 
        open={isDialogOpen}
        onOpenChange={(open) => {
          setIsDialogOpen(open);
        }}
      >
        <DialogContent className="w-[90%] h-[90vh] max-w-7xl p-4 sm:p-6" closeButtonClassName="sr-only hidden">
          <DialogHeader className="mb-4">
            <DialogTitle>Bildvorschau</DialogTitle>
            <DialogDescription>
              Ansehen und herunterladen des Bildes in voller Auflösung
            </DialogDescription>
          </DialogHeader>
          <div className="flex-1 h-[calc(90vh-8rem)]">
            <ImageDialog 
              sections={sections}
              currentSectionIndex={currentSectionIndex}
              currentImageIndex={currentImageIndex}
              commentCounts={commentCounts}
              selectedTab={selectedTab}
              setSelectedTab={setSelectedTab}
              isFullImageLoaded={isFullImageLoaded}
              setIsFullImageLoaded={setIsFullImageLoaded}
              handleDownload={handleDownload}
              isDownloading={isDownloading}
              onNavigate={handleNavigate}
              onClose={() => setIsDialogOpen(false)}
              favorites={favorites}
              toggleFavorite={toggleFavorite}
              onCommentAdded={(photoId) => {
                setCommentCounts(prev => ({
                  ...prev,
                  [photoId]: (prev[photoId] || 0) + 1
                }));
              }}
            />
          </div>
        </DialogContent>
      </Dialog>

      {sections.map((section, sectionIndex) => {
        // Count how many favorites are in this section
        const sectionFavorites = section.images.reduce((count, _, imageIndex) => {
          const photoId = `${section.title}-${imageIndex}`;
          return favorites[photoId] ? count + 1 : count;
        }, 0);

        // Count how many images with comments are in this section
        const sectionComments = section.images.reduce((count, _, imageIndex) => {
          const photoId = `${section.title}-${imageIndex}`;
          return commentCounts[photoId] ? count + 1 : count;
        }, 0);

        // Skip rendering the entire section if filtering and no matching items in this section
        if ((showFavoritesOnly && sectionFavorites === 0) || 
           (showCommentsOnly && sectionComments === 0)) {
          return null;
        }

        return (
          <div key={sectionIndex} id={`section-${section.title}`} className="space-y-4">
            <div className="space-y-2">
              <h2 className="text-2xl font-bold flex items-center gap-2">
                {section.title}
                {showFavoritesOnly && sectionFavorites > 0 && (
                  <span className="text-sm text-pink-500 font-normal">
                    ({sectionFavorites} {sectionFavorites === 1 ? 'Favorit' : 'Favoriten'})
                  </span>
                )}
                {showCommentsOnly && sectionComments > 0 && (
                  <span className="text-sm text-blue-500 font-normal">
                    ({sectionComments} {sectionComments === 1 ? 'Kommentar' : 'Kommentare'})
                  </span>
                )}
              </h2>
              {section.description && (
                <p className="text-muted-foreground">{section.description}</p>
              )}
            </div>
            
            <div className={`grid ${getGridClasses()} gap-4`}>
              {section.images.map((image, imageIndex) => {
                const photoId = `${section.title}-${imageIndex}`;
                const commentCount = commentCounts[photoId] || 0;
                const imageType = getImageType(image);
                const isFavorite = favorites[photoId] || false;
                
                // Skip non-favorite images when filter is active
                if ((showFavoritesOnly && !isFavorite) || 
                    (showCommentsOnly && commentCount === 0)) {
                  return null;
                }
                
                return (
                  <div
                    key={imageIndex}
                    ref={imageRefs.current[photoId]}
                    className={`
                      relative overflow-hidden ${roundedImages ? 'rounded-lg' : ''} bg-muted/30 transition-all duration-300
                      ${imageType === 'landscape' ? 'col-span-2' : ''} 
                      ${isFavorite ? 'ring-2 ring-pink-500/50 ring-offset-2 ring-offset-pink-500/30 shadow-lg shadow-pink-500/10' : ''}
                      ${commentCount > 0 && showCommentsOnly ? 'ring-2 ring-blue-500/50 ring-offset-2 ring-offset-blue-500/30 shadow-lg shadow-blue-500/10' : ''}
                    `}
                    style={{
                      aspectRatio: image.width / image.height
                    }}
                  >
                    <img
                      src={image.thumbnailSrc}
                      alt={image.alt}
                      className={`h-full w-full object-cover transition-all duration-300 ${isFavorite ? 'brightness-105' : ''}`}
                      loading="lazy"
                    />

                    {/* Main trigger for opening dialog */}
                    <div 
                      className="absolute inset-0 flex items-center justify-center gap-2 bg-black/60 opacity-0 hover:opacity-100 transition-opacity duration-200 cursor-zoom-in"
                      onClick={() => {
                        setCurrentSectionIndex(sectionIndex);
                        setCurrentImageIndex(imageIndex);
                        setSelectedTab("preview");
                        setIsFullImageLoaded(false);
                        setIsDialogOpen(true);
                      }}
                    >
                      <Button 
                        variant="secondary" 
                        size="icon" 
                        className="h-9 w-9 z-10"
                        onClick={(e) => {
                          e.stopPropagation();
                          handleDownload(image.src);
                        }}
                      >
                        <Download className="h-4 w-4" />
                      </Button>
                    </div>

                    {/* Favorite heart icon */}
                    <div 
                      className="absolute bottom-2 left-2 z-10"
                      onClick={(e) => toggleFavorite(photoId, e)}
                    >
                      <div className={`
                        rounded-full p-1.5 flex items-center justify-center cursor-pointer
                        ${isFavorite ? 'bg-pink-500/20 backdrop-blur-sm' : 'bg-black/30 hover:bg-black/50 backdrop-blur-sm'}
                        transition-all duration-300
                      `}>
                        <Heart 
                          className={`
                            ${isFavorite ? 
                              'text-pink-500 filter drop-shadow-sm' : 
                              'text-white/90'}
                            transition-all duration-300
                            ${gridSize <= 3 ? 'h-5 w-5' : 'h-4 w-4'}
                            ${isFavorite ? 'scale-110' : 'scale-100'}
                          `}
                          fill={isFavorite ? 'currentColor' : 'none'}
                          style={{
                            filter: isFavorite ? 'drop-shadow(0 0 2px rgba(236, 72, 153, 0.3))' : 'none',
                          }}
                        />
                      </div>
                    </div>

                    {/* Comment count indicator that also opens dialog */}
                    {commentCount > 0 && (
                      <div 
                        className="absolute bottom-2 right-2 z-10"
                        onClick={(e) => {
                          e.stopPropagation();
                          setCurrentSectionIndex(sectionIndex);
                          setCurrentImageIndex(imageIndex);
                          setSelectedTab("comments");
                          setIsDialogOpen(true);
                        }}
                      >
                        <div className={`
                          rounded-full p-1.5 flex items-center justify-center cursor-pointer
                          bg-black/30 hover:bg-black/50 backdrop-blur-sm
                          transition-all duration-300
                        `}>
                          <MessageCircle 
                            className={`
                              text-white/90
                              transition-all duration-300
                              ${gridSize <= 3 ? 'h-5 w-5' : 'h-4 w-4'}
                            `}
                          />
                          <span className="ml-1 text-white text-xs">{commentCount}</span>
                        </div>
                      </div>
                    )}
                  </div>
                );
              })}
            </div>
          </div>
        );
      })}

      {/* Show message when no favorites are found */}
      {showFavoritesOnly && !Object.values(favorites).some(v => v) && (
        <div className="bg-muted/30 rounded-lg p-8 text-center">
          <div className="inline-flex items-center justify-center p-4 rounded-full bg-pink-500/10 mb-4">
            <Heart className="h-8 w-8 text-pink-500/50" />
          </div>
          <h3 className="text-xl font-semibold mb-2">Keine Favoriten gefunden</h3>
          <p className="text-muted-foreground">
            Klicken Sie auf das Herz-Symbol bei den Bildern, um sie zu deinen Favoriten hinzuzufügen.
          </p>
        </div>
      )}

      {/* Show message when no commented images are found */}
      {showCommentsOnly && !Object.keys(commentCounts).length && (
        <div className="bg-muted/30 rounded-lg p-8 text-center">
          <div className="inline-flex items-center justify-center p-4 rounded-full bg-blue-500/10 mb-4">
            <MessageCircle className="h-8 w-8 text-blue-500/50" />
          </div>
          <h3 className="text-xl font-semibold mb-2">Keine kommentierte Bilder gefunden</h3>
          <p className="text-muted-foreground">
            Schreibe Kommentare zu Bildern, um sie hier anzuzeigen.
          </p>
        </div>
      )}

      {/* Scroll aware toolbar */}
      <ScrollToolbar 
        gridSize={gridSize} 
        setGridSize={setGridSize} 
        sections={sections} 
        inView={inView}
        clientConfig={clientConfig}
        favorites={favorites}
        showFavoritesOnly={showFavoritesOnly}
        setShowFavoritesOnly={setShowFavoritesOnly}
        commentCounts={commentCounts}
        showCommentsOnly={showCommentsOnly}
        setShowCommentsOnly={setShowCommentsOnly}
        handleDownloadAllFavorites={handleDownloadAllFavorites}
        handleResetAllFavorites={handleResetAllFavorites}
        handleCreateSharedLink={handleCreateSharedLink}
        clientId={window.location.pathname.split('/')[1]}
      />
      
      {/* Invisible ref element to detect when we're at the bottom */}
      <div ref={footerRef} className="h-1" />
      
      {/* CSS to fix image layout */}
      <style jsx global>{`
        .grid > div {
          display: flex;
          overflow: hidden;
        }
        
        .grid > div img {
          object-fit: cover;
          width: 100%;
          height: 100%;
          max-width: 100%;
          max-height: 100%;
        }
      `}</style>
    </div>
  );
}
Editor is loading...
Leave a Comment