60 lines
1.6 KiB
TypeScript
60 lines
1.6 KiB
TypeScript
|
|
import { useState, useEffect } from 'react';
|
||
|
|
import { apiClient } from '@/api/client';
|
||
|
|
|
||
|
|
interface SuggestionBadgeProps {
|
||
|
|
onClick: () => void;
|
||
|
|
}
|
||
|
|
|
||
|
|
export function SuggestionBadge({ onClick }: SuggestionBadgeProps) {
|
||
|
|
const [count, setCount] = useState(0);
|
||
|
|
const [pulsing, setPulsing] = useState(false);
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
// Initial count
|
||
|
|
apiClient.get('/api/v1/ai-proactive/suggestions').then(r => {
|
||
|
|
setCount(r.data.items.length);
|
||
|
|
}).catch(() => {});
|
||
|
|
|
||
|
|
// SSE for real-time updates
|
||
|
|
const eventSource = new EventSource('/api/v1/ai-proactive/suggestions/stream');
|
||
|
|
eventSource.onmessage = () => {
|
||
|
|
setCount(prev => prev + 1);
|
||
|
|
setPulsing(true);
|
||
|
|
setTimeout(() => setPulsing(false), 2000);
|
||
|
|
};
|
||
|
|
|
||
|
|
return () => eventSource.close();
|
||
|
|
}, []);
|
||
|
|
|
||
|
|
if (count === 0) {
|
||
|
|
return (
|
||
|
|
<button
|
||
|
|
onClick={onClick}
|
||
|
|
className="relative p-2 text-gray-500 hover:text-gray-700 transition-colors"
|
||
|
|
title="KI Vorschläge"
|
||
|
|
>
|
||
|
|
🤖
|
||
|
|
</button>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
return (
|
||
|
|
<button
|
||
|
|
onClick={onClick}
|
||
|
|
className={`relative p-2 text-gray-500 hover:text-gray-700 transition-colors ${
|
||
|
|
pulsing ? 'animate-pulse' : ''
|
||
|
|
}`}
|
||
|
|
title={`${count} KI Vorschläge`}
|
||
|
|
>
|
||
|
|
🤖
|
||
|
|
<span
|
||
|
|
className={`absolute -top-0 -right-0 min-w-[18px] h-[18px] flex items-center justify-center text-[10px] font-bold text-white rounded-full ${
|
||
|
|
pulsing ? 'bg-red-500 animate-bounce' : 'bg-blue-500'
|
||
|
|
}`}
|
||
|
|
>
|
||
|
|
{count > 99 ? '99+' : count}
|
||
|
|
</span>
|
||
|
|
</button>
|
||
|
|
);
|
||
|
|
}
|