"use client"; import { useState, useRef, useEffect, useCallback } from "react"; import { motion, AnimatePresence } from "framer-motion"; import { Bell, Check, CheckCheck, Trash2, Film, AlertTriangle, CreditCard, Info, X } from "lucide-react"; import { useNotifications, useUnreadNotificationCount, useMarkNotificationAsRead, useMarkAllNotificationsAsRead, useDeleteNotification, } from "@/hooks/use-api"; import { cn } from "@/lib/utils"; import type { Notification } from "@/lib/api/api-service"; /** Bildirim tipine göre ikon ve renk */ function getNotificationMeta(type: string) { switch (type) { case "render_complete": return { icon: Film, color: "text-emerald-400", bg: "bg-emerald-500/12" }; case "render_failed": return { icon: AlertTriangle, color: "text-red-400", bg: "bg-red-500/12" }; case "credit_low": case "subscription_changed": return { icon: CreditCard, color: "text-amber-400", bg: "bg-amber-500/12" }; default: return { icon: Info, color: "text-violet-400", bg: "bg-violet-500/12" }; } } /** Tarih formatı — relative time */ function timeAgo(dateStr: string): string { const now = Date.now(); const date = new Date(dateStr).getTime(); const diff = Math.max(0, now - date); const seconds = Math.floor(diff / 1000); const minutes = Math.floor(seconds / 60); const hours = Math.floor(minutes / 60); const days = Math.floor(hours / 24); if (seconds < 60) return "az önce"; if (minutes < 60) return `${minutes} dk önce`; if (hours < 24) return `${hours} sa önce`; if (days < 7) return `${days} gün önce`; return new Date(dateStr).toLocaleDateString("tr-TR", { day: "numeric", month: "short" }); } function NotificationItem({ notification, onMarkRead, onDelete, }: { notification: Notification; onMarkRead: (id: string) => void; onDelete: (id: string) => void; }) { const meta = getNotificationMeta(notification.type); const Icon = meta.icon; return ( {/* İkon */}
{/* İçerik */}

{notification.title}

{notification.message && (

{notification.message}

)}

{timeAgo(notification.createdAt)}

{/* Aksiyonlar — hover'da göster */}
{!notification.isRead && ( )}
{/* Okunmamış göstergesi */} {!notification.isRead && ( )}
); } export function NotificationsDropdown() { const [isOpen, setIsOpen] = useState(false); const dropdownRef = useRef(null); const { data: unreadData } = useUnreadNotificationCount(); const { data: notifData, isLoading } = useNotifications({ limit: 20 }); const markRead = useMarkNotificationAsRead(); const markAllRead = useMarkAllNotificationsAsRead(); const deleteNotif = useDeleteNotification(); // Okunmamış sayısı — global interceptor wrap edebilir // eslint-disable-next-line @typescript-eslint/no-explicit-any const unreadCount = (unreadData as any)?.data?.count ?? (unreadData as any)?.count ?? 0; // eslint-disable-next-line @typescript-eslint/no-explicit-any const notifications: Notification[] = (notifData as any)?.data?.data ?? (notifData as any)?.data ?? []; // Dışarı tıklanınca kapat const handleClickOutside = useCallback((e: MouseEvent) => { if (dropdownRef.current && !dropdownRef.current.contains(e.target as Node)) { setIsOpen(false); } }, []); useEffect(() => { if (isOpen) { document.addEventListener("mousedown", handleClickOutside); } return () => document.removeEventListener("mousedown", handleClickOutside); }, [isOpen, handleClickOutside]); return (
{/* Tetikleyici Buton */} {/* Dropdown Panel */} {isOpen && ( {/* Başlık */}

Bildirimler

{unreadCount > 0 && ( )}
{/* Bildirim Listesi */}
{isLoading ? (

Yükleniyor...

) : notifications.length === 0 ? (

Henüz bildirim yok

Video render ve sistem olayları burada görünecek

) : ( {notifications.map((n) => ( markRead.mutate(id)} onDelete={(id) => deleteNotif.mutate(id)} /> ))} )}
)}
); }