170 lines
5.3 KiB
TypeScript
170 lines
5.3 KiB
TypeScript
"use client";
|
|
|
|
import {
|
|
Bell,
|
|
ExternalLink,
|
|
MessageSquare,
|
|
ShoppingCart,
|
|
TrendingUp,
|
|
} from "lucide-react";
|
|
import Link from "next/link";
|
|
import { useLocale } from "next-intl";
|
|
import { Button } from "@/components/ui/button";
|
|
// ScrollArea não disponível, usando div com overflow
|
|
import { Separator } from "@/components/ui/separator";
|
|
|
|
interface Notification {
|
|
notification_id: number;
|
|
notification_type: string;
|
|
title: string;
|
|
message: string;
|
|
is_read: boolean;
|
|
related_id: number | null;
|
|
created_at: string;
|
|
}
|
|
|
|
interface NotificationDropdownProps {
|
|
notifications: Notification[];
|
|
onMarkAsRead: (notificationId: number) => Promise<void>;
|
|
onViewAll?: () => void;
|
|
}
|
|
|
|
function formatTimeAgo(date: Date): string {
|
|
const seconds = Math.floor((Date.now() - date.getTime()) / 1000);
|
|
if (seconds < 60) return "há alguns segundos";
|
|
const minutes = Math.floor(seconds / 60);
|
|
if (minutes < 60)
|
|
return `há ${minutes} ${minutes === 1 ? "minuto" : "minutos"}`;
|
|
const hours = Math.floor(minutes / 60);
|
|
if (hours < 24) return `há ${hours} ${hours === 1 ? "hora" : "horas"}`;
|
|
const days = Math.floor(hours / 24);
|
|
if (days < 30) return `há ${days} ${days === 1 ? "dia" : "dias"}`;
|
|
const months = Math.floor(days / 30);
|
|
if (months < 12) return `há ${months} ${months === 1 ? "mês" : "meses"}`;
|
|
const years = Math.floor(months / 12);
|
|
return `há ${years} ${years === 1 ? "ano" : "anos"}`;
|
|
}
|
|
|
|
function getNotificationIcon(type: string) {
|
|
switch (type) {
|
|
case "purchase":
|
|
case "sale":
|
|
return <ShoppingCart className="h-4 w-4" />;
|
|
case "bid":
|
|
return <TrendingUp className="h-4 w-4" />;
|
|
case "system":
|
|
return <Bell className="h-4 w-4" />;
|
|
default:
|
|
return <MessageSquare className="h-4 w-4" />;
|
|
}
|
|
}
|
|
|
|
function getNotificationColor(type: string) {
|
|
switch (type) {
|
|
case "purchase":
|
|
case "sale":
|
|
return "bg-green-500";
|
|
case "bid":
|
|
return "bg-blue-500";
|
|
case "system":
|
|
return "bg-yellow-500";
|
|
default:
|
|
return "bg-gray-500";
|
|
}
|
|
}
|
|
|
|
export function NotificationDropdown({
|
|
notifications,
|
|
onMarkAsRead,
|
|
onViewAll,
|
|
}: NotificationDropdownProps) {
|
|
const locale = useLocale();
|
|
|
|
const handleNotificationClick = async (notification: Notification) => {
|
|
if (!notification.is_read) {
|
|
await onMarkAsRead(notification.notification_id);
|
|
}
|
|
};
|
|
|
|
if (notifications.length === 0) {
|
|
return (
|
|
<div className="w-80 p-4 text-center text-sm text-muted-foreground">
|
|
Nenhuma notificação
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="w-80 flex flex-col">
|
|
<div className="p-4 border-b">
|
|
<div className="flex items-center justify-between">
|
|
<h3 className="font-semibold text-sm">Notificações</h3>
|
|
{onViewAll && (
|
|
<Button
|
|
variant="ghost"
|
|
size="sm"
|
|
onClick={onViewAll}
|
|
className="h-auto p-0 text-xs"
|
|
>
|
|
Ver todas
|
|
</Button>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="h-[400px] overflow-y-auto">
|
|
<div className="p-2">
|
|
{notifications.map((notification, index) => (
|
|
<div key={notification.notification_id}>
|
|
<button
|
|
onClick={() => handleNotificationClick(notification)}
|
|
className={`w-full text-left p-3 rounded-lg hover:bg-muted transition-colors ${
|
|
!notification.is_read ? "bg-muted/50" : ""
|
|
}`}
|
|
>
|
|
<div className="flex items-start gap-3">
|
|
<div
|
|
className={`${getNotificationColor(
|
|
notification.notification_type,
|
|
)} rounded-full p-2 text-white flex-shrink-0`}
|
|
>
|
|
{getNotificationIcon(notification.notification_type)}
|
|
</div>
|
|
<div className="flex-1 min-w-0">
|
|
<div className="flex items-start justify-between gap-2">
|
|
<h4 className="font-medium text-sm">
|
|
{notification.title}
|
|
</h4>
|
|
{!notification.is_read && (
|
|
<div className="h-2 w-2 bg-primary rounded-full flex-shrink-0 mt-1" />
|
|
)}
|
|
</div>
|
|
<p className="text-xs text-muted-foreground mt-1 line-clamp-2">
|
|
{notification.message}
|
|
</p>
|
|
<p className="text-xs text-muted-foreground mt-1">
|
|
{formatTimeAgo(new Date(notification.created_at))}
|
|
</p>
|
|
{notification.related_id && (
|
|
<Link
|
|
href={`/${locale}/market`}
|
|
onClick={(e) => e.stopPropagation()}
|
|
className="text-xs text-primary hover:underline mt-1 inline-flex items-center gap-1"
|
|
>
|
|
Ver anúncio <ExternalLink className="h-3 w-3" />
|
|
</Link>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</button>
|
|
{index < notifications.length - 1 && (
|
|
<Separator className="my-2" />
|
|
)}
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|