162 lines
5.0 KiB
TypeScript
162 lines
5.0 KiB
TypeScript
"use client";
|
|
|
|
import { useState, useEffect, ReactNode } from 'react';
|
|
import { X, LucideIcon } from 'lucide-react';
|
|
import { Button } from '@/components/ui/button';
|
|
import { useRouter } from 'next/navigation';
|
|
|
|
export interface OutlookNotificationAction {
|
|
label: string;
|
|
onClick: () => void;
|
|
variant?: 'default' | 'destructive' | 'outline' | 'secondary' | 'ghost' | 'link';
|
|
className?: string;
|
|
icon?: LucideIcon;
|
|
}
|
|
|
|
export interface OutlookNotificationData {
|
|
id: string;
|
|
source: 'email' | 'rocketchat' | 'call' | 'leantime' | 'calendar';
|
|
title: string;
|
|
subtitle?: string;
|
|
message: string;
|
|
icon: LucideIcon;
|
|
iconColor?: string;
|
|
iconBgColor?: string;
|
|
borderColor?: string;
|
|
link?: string;
|
|
timestamp?: Date;
|
|
actions?: OutlookNotificationAction[];
|
|
autoDismiss?: number; // milliseconds, default 30s
|
|
}
|
|
|
|
interface OutlookNotificationProps {
|
|
notification: OutlookNotificationData | null;
|
|
onDismiss: () => void;
|
|
}
|
|
|
|
export function OutlookNotification({
|
|
notification,
|
|
onDismiss,
|
|
}: OutlookNotificationProps) {
|
|
const router = useRouter();
|
|
const [isVisible, setIsVisible] = useState(false);
|
|
|
|
useEffect(() => {
|
|
if (notification) {
|
|
console.log('[OutlookNotification] 📬 Showing notification', {
|
|
source: notification.source,
|
|
title: notification.title,
|
|
});
|
|
setIsVisible(true);
|
|
|
|
// Auto-dismiss after specified time (default 30 seconds)
|
|
const autoDismissTime = notification.autoDismiss || 30000;
|
|
const autoDismissTimer = setTimeout(() => {
|
|
console.log('[OutlookNotification] ⏰ Auto-dismissing after', autoDismissTime, 'ms');
|
|
setIsVisible(false);
|
|
onDismiss();
|
|
}, autoDismissTime);
|
|
|
|
return () => {
|
|
clearTimeout(autoDismissTimer);
|
|
};
|
|
} else {
|
|
setIsVisible(false);
|
|
}
|
|
}, [notification, onDismiss]);
|
|
|
|
if (!notification || !isVisible) {
|
|
return null;
|
|
}
|
|
|
|
const handleDismiss = () => {
|
|
onDismiss();
|
|
setIsVisible(false);
|
|
};
|
|
|
|
const handleLinkClick = () => {
|
|
if (notification.link) {
|
|
router.push(notification.link);
|
|
setIsVisible(false);
|
|
onDismiss();
|
|
}
|
|
};
|
|
|
|
const Icon = notification.icon;
|
|
const borderColor = notification.borderColor || 'border-blue-500';
|
|
const iconBgColor = notification.iconBgColor || 'bg-blue-100';
|
|
const iconColor = notification.iconColor || 'text-blue-600';
|
|
|
|
return (
|
|
<div className={`bg-white rounded-lg shadow-2xl border-2 ${borderColor} p-5 min-w-[340px] max-w-[420px]`}>
|
|
{/* Header with Outlook-like style */}
|
|
<div className="flex items-start justify-between mb-4 pb-3 border-b border-gray-200">
|
|
<div className="flex items-center gap-3">
|
|
<div className={`w-10 h-10 rounded-full ${iconBgColor} flex items-center justify-center flex-shrink-0`}>
|
|
<Icon className={`w-5 h-5 ${iconColor}`} />
|
|
</div>
|
|
<div>
|
|
<h3 className="font-semibold text-base text-gray-900 leading-tight">{notification.title}</h3>
|
|
{notification.subtitle && (
|
|
<p className="text-xs text-gray-500 mt-0.5">{notification.subtitle}</p>
|
|
)}
|
|
</div>
|
|
</div>
|
|
<button
|
|
onClick={handleDismiss}
|
|
className="text-gray-400 hover:text-gray-600 transition-colors p-1 -mt-1 -mr-1"
|
|
aria-label="Fermer"
|
|
>
|
|
<X className="w-4 h-4" />
|
|
</button>
|
|
</div>
|
|
|
|
{/* Message - Outlook style */}
|
|
<div className="mb-5">
|
|
<p className="text-gray-800 text-sm leading-relaxed">
|
|
{notification.message}
|
|
</p>
|
|
{notification.timestamp && (
|
|
<p className="text-xs text-gray-500 mt-1.5">
|
|
{new Date(notification.timestamp).toLocaleTimeString('fr-FR', {
|
|
hour: '2-digit',
|
|
minute: '2-digit'
|
|
})}
|
|
</p>
|
|
)}
|
|
</div>
|
|
|
|
{/* Actions - Outlook style buttons */}
|
|
{notification.actions && notification.actions.length > 0 && (
|
|
<div className="flex gap-2.5">
|
|
{notification.actions.map((action, index) => {
|
|
const ActionIcon = action.icon;
|
|
return (
|
|
<Button
|
|
key={index}
|
|
onClick={action.onClick}
|
|
variant={action.variant || 'default'}
|
|
className={`flex-1 font-medium py-2.5 text-sm shadow-sm ${action.className || ''}`}
|
|
>
|
|
{ActionIcon && <ActionIcon className="w-4 h-4 mr-2" />}
|
|
{action.label}
|
|
</Button>
|
|
);
|
|
})}
|
|
</div>
|
|
)}
|
|
|
|
{/* If no actions but has link, make the whole card clickable */}
|
|
{!notification.actions && notification.link && (
|
|
<Button
|
|
onClick={handleLinkClick}
|
|
className="w-full font-medium py-2.5 text-sm shadow-sm"
|
|
>
|
|
Ouvrir
|
|
</Button>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|