Untitled

 avatar
unknown
plain_text
11 days ago
32 kB
4
Indexable
// /components/gallery.tsx
"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,
  DialogHeader,
  DialogTitle,
  DialogDescription,
  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;

  const handleImageClick = () => {
    if (isFullImageLoaded) {
      window.open(fullSizeImageSrc, '_blank');
    }
  };

  return (
    <div className="flex flex-col h-full">
      <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">
              {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>
              )}

              {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>
              )}

              <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)}
              />

              <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>

        <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);

  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);

  const imageRefs = useRef<{ [key: string]: React.RefObject<HTMLDivElement> }>({});
  const footerRef = useRef(null);
  const inView = useInView(footerRef, { amount: 0.1 });
  const [activeDialogId, setActiveDialogId] = useState<string | null>(null);

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

  const scrollToCurrentImage = () => {
    const photoId = `${sections[currentSectionIndex].title}-${currentImageIndex}`;
    const ref = imageRefs.current[photoId];
    if (ref && ref.current) {
      setTimeout(() => {
        ref.current?.scrollIntoView({
          behavior: 'smooth',
          block: 'center'
        });
      }, 100);
    }
  };

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

  useEffect(() => {
    if (isSelectionView && sections.length > 0) {
      const selectionFavorites = { ...favorites };
      sections.forEach((section) => {
        section.images.forEach((_, imageIndex) => {
          const photoId = `${section.title}-${imageIndex}`;
          if (selectionFavorites[photoId] === undefined) {
            selectionFavorites[photoId] = true;
          }
        });
      });
      setFavorites(selectionFavorites);
    }
  }, [isSelectionView, sections]);

  const toggleFavorite = (photoId: string, e: React.MouseEvent) => {
    e.stopPropagation();
    const newFavorites = { ...favorites };
    newFavorites[photoId] = !newFavorites[photoId];
    setFavorites(newFavorites);
    localStorage.setItem('favorites', JSON.stringify(newFavorites));
  };

  const handleResetAllFavorites = () => {
    setFavorites({});
    localStorage.removeItem('favorites');
    toast.success('Alle Favoriten wurden zurückgesetzt');
    if (showFavoritesOnly) {
      setShowFavoritesOnly(false);
    }
  };

  const handleCreateSharedLink = async (password: string, requirePassword: boolean) => {
    const clientId = window.location.pathname.split('/')[1];
    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);
        }
      });
    });

    const selectionData = {
      favorites: selectedFavorites,
      requirePassword,
      password: requirePassword ? password : "",
      createdAt: new Date().toISOString(),
      parentClientId: clientId
    };

    try {
      let nextSelectionId = 1;
      const response = await fetch(`/api/selections?clientId=${encodeURIComponent(clientId)}`);
      if (response.ok) {
        const selections = await response.json();
        if (selections && selections.length > 0) {
          const numericIds = selections
            .map(s => parseInt(s.selection_id))
            .filter(id => !isNaN(id));
          if (numericIds.length > 0) {
            nextSelectionId = Math.max(...numericIds) + 1;
          }
        }
      }

      const saveResponse = await fetch(`/api/selections`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          clientId,
          selectionId: nextSelectionId.toString(),
          data: selectionData
        }),
      });

      if (!saveResponse.ok) throw new Error("Failed to create selection");

      const origin = window.location.origin;
      const shareableUrl = `${origin}/${clientId}/auswahl/${nextSelectionId}`;
      return requirePassword ? `${shareableUrl}#password=${password}` : shareableUrl;
    } catch (error) {
      console.error("Error creating shared link:", error);
      throw error;
    }
  };

  useEffect(() => {
    if (!isDialogOpen) {
      setSelectedTab("preview");
      setIsFullImageLoaded(false);
      setActiveDialogId(null);
      setTimeout(scrollToCurrentImage, 200);
    }
  }, [isDialogOpen]);

  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();
  }, []);

  useEffect(() => {
    setIsFullImageLoaded(false);
  }, [currentImageIndex]);

  const handleDownloadAllFavorites = async () => {
    const favoritePhotos = [];
    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;
    }

    toast.info(`Download von ${favoritePhotos.length} Favoriten wird gestartet...`);
    let downloadedCount = 0;
    let failedCount = 0;

    for (const photo of favoritePhotos) {
      try {
        await new Promise(resolve => setTimeout(resolve, 300));
        const response = await fetch(photo.src, { 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++;
        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++;
      }
    }

    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, { 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);
    }
  };

  const handleNavigate = (direction: 'prev' | 'next') => {
    if (direction === 'prev' && currentImageIndex > 0) {
      setCurrentImageIndex(currentImageIndex - 1);
      setTimeout(scrollToCurrentImage, 100);
    } else if (direction === 'next' && currentImageIndex < sections[currentSectionIndex].images.length - 1) {
      setCurrentImageIndex(currentImageIndex + 1);
      setTimeout(scrollToCurrentImage, 100);
    }
  };

  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]);

  // Define responsive grid classes
  const getGridClasses = () => {
    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];
  };

  // Calculate row span based on image height
  const getRowSpan = (width: number, height: number) => {
    const aspectRatio = height / width;
    const baseHeight = 200; // Base height for row calculation
    const rowHeight = 10; // Fixed row height in grid (grid-auto-rows)
    const calculatedHeight = baseHeight * aspectRatio;
    return Math.ceil(calculatedHeight / rowHeight);
  };

  return (
    <div className="space-y-12">
      <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) => {
        const sectionFavorites = section.images.reduce((count, _, imageIndex) => {
          const photoId = `${section.title}-${imageIndex}`;
          return favorites[photoId] ? count + 1 : count;
        }, 0);

        const sectionComments = section.images.reduce((count, _, imageIndex) => {
          const photoId = `${section.title}-${imageIndex}`;
          return commentCounts[photoId] ? count + 1 : count;
        }, 0);

        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 grid-auto-rows-[10px]`}>
              {section.images.map((image, imageIndex) => {
                const photoId = `${section.title}-${imageIndex}`;
                const commentCount = commentCounts[photoId] || 0;
                const isFavorite = favorites[photoId] || false;
                const rowSpan = getRowSpan(image.width, image.height);

                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
                      ${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={{ gridRow: `span ${rowSpan}` }}
                  >
                    <img
                      src={image.thumbnailSrc || image.src}
                      alt={image.alt}
                      className="w-full h-full object-cover"
                      loading="lazy"
                    />

                    <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>

                    <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>

                    {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>
        );
      })}

      {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>
      )}

      {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>
      )}

      <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]}
      />

      <div ref={footerRef} className="h-1" />
    </div>
  );
}
Editor is loading...
Leave a Comment