Untitled

 avatar
unknown
plain_text
17 days ago
8.3 kB
4
Indexable
import { useState, useEffect } from "react"
import { StarIcon, MessageCircle } from "lucide-react"
import { motion, AnimatePresence } from "framer-motion"
import { useParams } from "next/navigation"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Textarea } from "@/components/ui/textarea"
import { Avatar, AvatarFallback } from "@/components/ui/avatar"
import { toast } from "sonner"

// Updated interface to work with both snake_case (from Supabase) and camelCase
interface Comment {
  id: string
  photoId?: string
  photo_id?: string
  name: string
  rating: number | null
  text: string
  createdAt?: string
  created_at?: string
}

interface PhotoCommentsProps {
  photoId: string;
  onCommentAdded?: () => void;
}

const funnyNames = [
  "LustigerLuchs",
  "VerspielterVogel",
  "QuirlenderQualle",
  "SchmunzelnderSchmetterling",
  "TanzenderTiger",
  "GrinsenderGorilla",
  "KichernderKoala",
  "WitzigerWaschbär",
  "AlbernerAffe",
  "LachenderLöwe",
  "PossierlicherPinguin",
  "ScherzenderSchwan",
]

const generateGradient = (name: string) => {
  const hue1 = Array.from(name).reduce((acc, char) => acc + char.charCodeAt(0), 0) % 360;
  const hue2 = (hue1 + 40) % 360;
  
  return `linear-gradient(135deg, 
    hsl(${hue1}, 70%, 60%) 0%, 
    hsl(${hue2}, 70%, 60%) 100%
  )`;
};

export function PhotoComments({ photoId, onCommentAdded }: PhotoCommentsProps) {
  const params = useParams();
  const clientId = params.clientId as string;
  const [comments, setComments] = useState<Comment[]>([])
  const [name, setName] = useState("")
  const [rating, setRating] = useState<number | null>(null)
  const [comment, setComment] = useState("")
  const [isSubmitting, setIsSubmitting] = useState(false)

  useEffect(() => {
    fetchComments()
  }, [photoId, clientId])

  const fetchComments = async () => {
    try {
      const response = await fetch(
        `/api/comments?photoId=${encodeURIComponent(photoId)}&clientId=${encodeURIComponent(clientId)}`
      );
      if (!response.ok) throw new Error('Failed to fetch comments');
      const data = await response.json();
      setComments(data);
    } catch (error) {
      console.error('Error fetching comments:', error);
      toast.error('Fehler beim Laden der Kommentare');
    }
  }

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault()
    setIsSubmitting(true)

    try {
      const finalName = name || funnyNames[Math.floor(Math.random() * funnyNames.length)]
      
      const response = await fetch('/api/comments', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          photoId,
          clientId,
          name: finalName,
          rating,
          text: comment,
        }),
      })

      if (!response.ok) throw new Error('Failed to submit comment');

      const newComment = await response.json();
      setComments(prev => [newComment, ...prev]);
      setName("")
      setRating(null)
      setComment("")
      toast.success('Kommentar erfolgreich hinzugefügt');
      onCommentAdded?.();
    } catch (error) {
      console.error('Error submitting comment:', error);
      toast.error('Fehler beim Speichern des Kommentars');
    } finally {
      setIsSubmitting(false)
    }
  }

  const formatDate = (dateString: string) => {
    const date = new Date(dateString);
    return new Intl.DateTimeFormat('de-DE', { 
      day: '2-digit',
      month: '2-digit',
      year: 'numeric',
      hour: '2-digit',
      minute: '2-digit'
    }).format(date);
  };

  // Helper function to handle both snake_case and camelCase properties
  const getCommentDate = (comment: Comment) => {
    return comment.created_at || comment.createdAt || '';
  };

  const getCommentPhotoId = (comment: Comment) => {
    return comment.photo_id || comment.photoId || '';
  };

  return (
    <div className="space-y-6">
      <h3 className="text-lg font-semibold">Kommentare</h3>
      <form onSubmit={handleSubmit} className="space-y-4">
        <Input 
          placeholder="Ihr Name (optional)" 
          value={name} 
          onChange={(e) => setName(e.target.value)} 
        />
        <div className="flex items-center space-x-1">
          {[1, 2, 3, 4, 5].map((star) => (
            <Button
              key={star}
              type="button"
              variant="ghost"
              size="sm"
              className="p-0 hover:bg-transparent"
              onClick={() => setRating(star)}
            >
              <StarIcon 
                fill={rating && rating >= star ? "currentColor" : "transparent"}
                className={`h-6 w-6 ${
                  rating && rating >= star 
                    ? "text-yellow-400 dark:text-yellow-300" 
                    : "text-gray-400 dark:text-gray-500"
                }`}
              />
            </Button>
          ))}
          {rating !== null && (
            <Button
              type="button"
              variant="ghost"
              size="sm"
              className="ml-2 text-sm text-muted-foreground hover:text-muted-foreground"
              onClick={() => setRating(null)}
            >
              Zurücksetzen
            </Button>
          )}
        </div>
        <Textarea 
          placeholder="Ihr Kommentar" 
          value={comment} 
          onChange={(e) => setComment(e.target.value)} 
          required 
          className="min-h-[100px]"
        />
        <Button type="submit" disabled={isSubmitting} className="w-full">
          {isSubmitting ? 'Wird gespeichert...' : 'Kommentar hinzufügen'}
        </Button>
      </form>

      <div className="space-y-4 pt-6">
        <AnimatePresence>
          {comments.map((c) => (
            <motion.div
              key={c.id}
              initial={{ opacity: 0, y: 20 }}
              animate={{ opacity: 1, y: 0 }}
              exit={{ opacity: 0, y: -20 }}
              className="rounded-lg border bg-card p-4 text-card-foreground shadow-sm"
            >
              <div className="flex flex-col space-y-3">
                <div className="flex items-start justify-between">
                  <div className="flex items-center space-x-3">
                    <Avatar>
                      <AvatarFallback 
                        style={{ background: generateGradient(c.name) }}
                        className="text-white font-medium"
                      >
                        {c.name[0].toUpperCase()}
                      </AvatarFallback>
                    </Avatar>
                    <div>
                      <p className="font-medium">{c.name}</p>
                      {c.rating !== null && c.rating > 0 && (
                        <div className="flex mt-1">
                          {[1, 2, 3, 4, 5].map((star) => (
                            <StarIcon 
                              key={star}
                              fill={c.rating >= star ? "currentColor" : "transparent"}
                              className={`h-4 w-4 ${
                                c.rating >= star 
                                  ? "text-yellow-400 dark:text-yellow-300" 
                                  : "text-gray-400 dark:text-gray-500"
                              }`}
                            />
                          ))}
                        </div>
                      )}
                    </div>
                  </div>
                  <span className="text-sm text-muted-foreground whitespace-nowrap">
                    {formatDate(getCommentDate(c))}
                  </span>
                </div>
                <p className="text-sm leading-relaxed">{c.text}</p>
              </div>
            </motion.div>
          ))}
        </AnimatePresence>
        {comments.length === 0 && (
          <p className="text-center text-muted-foreground text-sm py-4">
            Noch keine Kommentare. Seien Sie der Erste!
          </p>
        )}
      </div>
    </div>
  )
}

export default PhotoComments;
Editor is loading...
Leave a Comment