110 lines
3.5 KiB
TypeScript
110 lines
3.5 KiB
TypeScript
|
|
import { useState } from 'react';
|
||
|
|
import { useSuggestions } from './api';
|
||
|
|
import { SuggestionCard } from './SuggestionCard';
|
||
|
|
|
||
|
|
interface SuggestionSidebarProps {
|
||
|
|
isOpen: boolean;
|
||
|
|
onClose: () => void;
|
||
|
|
}
|
||
|
|
|
||
|
|
export function SuggestionSidebar({ isOpen, onClose }: SuggestionSidebarProps) {
|
||
|
|
const { suggestions, connected, dismiss, act } = useSuggestions();
|
||
|
|
const [filter, setFilter] = useState<string>('all');
|
||
|
|
|
||
|
|
const filteredSuggestions = filter === 'all'
|
||
|
|
? suggestions
|
||
|
|
: suggestions.filter(s => s.suggestion_type === filter);
|
||
|
|
|
||
|
|
const filterOptions = [
|
||
|
|
{ value: 'all', label: 'Alle' },
|
||
|
|
{ value: 'info', label: 'Info' },
|
||
|
|
{ value: 'warning', label: 'Warnung' },
|
||
|
|
{ value: 'action', label: 'Aktion' },
|
||
|
|
{ value: 'insight', label: 'Erkenntnis' },
|
||
|
|
];
|
||
|
|
|
||
|
|
return (
|
||
|
|
<>
|
||
|
|
{/* Overlay */}
|
||
|
|
{isOpen && (
|
||
|
|
<div
|
||
|
|
className="fixed inset-0 bg-black/20 z-40 lg:hidden"
|
||
|
|
onClick={onClose}
|
||
|
|
/>
|
||
|
|
)}
|
||
|
|
|
||
|
|
{/* Sidebar */}
|
||
|
|
<aside
|
||
|
|
className={`fixed right-0 top-0 h-full w-96 bg-white shadow-2xl z-50 transform transition-transform duration-300 flex flex-col ${
|
||
|
|
isOpen ? 'translate-x-0' : 'translate-x-full'
|
||
|
|
}`}
|
||
|
|
>
|
||
|
|
{/* Header */}
|
||
|
|
<div className="flex items-center justify-between p-4 border-b border-gray-200">
|
||
|
|
<div className="flex items-center gap-2">
|
||
|
|
<h2 className="font-semibold text-gray-800">KI Vorschläge</h2>
|
||
|
|
{connected && (
|
||
|
|
<span className="flex items-center gap-1 text-xs text-green-600">
|
||
|
|
<span className="w-2 h-2 bg-green-500 rounded-full animate-pulse" />
|
||
|
|
Live
|
||
|
|
</span>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
<button
|
||
|
|
onClick={onClose}
|
||
|
|
className="text-gray-400 hover:text-gray-600 transition-colors p-1"
|
||
|
|
>
|
||
|
|
✕
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* Filter */}
|
||
|
|
<div className="flex gap-1 p-3 border-b border-gray-100 overflow-x-auto">
|
||
|
|
{filterOptions.map(opt => (
|
||
|
|
<button
|
||
|
|
key={opt.value}
|
||
|
|
onClick={() => setFilter(opt.value)}
|
||
|
|
className={`px-3 py-1 rounded-full text-xs font-medium whitespace-nowrap transition-colors ${
|
||
|
|
filter === opt.value
|
||
|
|
? 'bg-blue-500 text-white'
|
||
|
|
: 'bg-gray-100 text-gray-600 hover:bg-gray-200'
|
||
|
|
}`}
|
||
|
|
>
|
||
|
|
{opt.label}
|
||
|
|
</button>
|
||
|
|
))}
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* Content */}
|
||
|
|
<div className="flex-1 overflow-y-auto p-4">
|
||
|
|
{filteredSuggestions.length === 0 ? (
|
||
|
|
<div className="flex flex-col items-center justify-center h-full text-gray-400">
|
||
|
|
<div className="text-4xl mb-3">🤖</div>
|
||
|
|
<p className="text-sm">Keine Vorschläge vorhanden</p>
|
||
|
|
<p className="text-xs mt-1">Die KI analysiert deinen Kontext...</p>
|
||
|
|
</div>
|
||
|
|
) : (
|
||
|
|
<div>
|
||
|
|
{filteredSuggestions.map(suggestion => (
|
||
|
|
<SuggestionCard
|
||
|
|
n key={suggestion.id}
|
||
|
|
suggestion={suggestion}
|
||
|
|
onDismiss={dismiss}
|
||
|
|
onAct={act}
|
||
|
|
/>
|
||
|
|
))}
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* Footer */}
|
||
|
|
<div className="p-3 border-t border-gray-200 text-center">
|
||
|
|
<span className="text-xs text-gray-400">
|
||
|
|
{suggestions.length} Vorschläge insgesamt
|
||
|
|
</span>
|
||
|
|
</div>
|
||
|
|
</aside>
|
||
|
|
</>
|
||
|
|
);
|
||
|
|
}
|