Services: Admin-Lehrer, Backend-Lehrer, Studio v2, Website, Klausur-Service, School-Service, Voice-Service, Geo-Service, BreakPilot Drive, Agent-Core Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
171 lines
5.2 KiB
TypeScript
171 lines
5.2 KiB
TypeScript
'use client'
|
|
|
|
import { ChevronRight, Clock, Lightbulb, ClipboardCheck, BookOpen, Calendar, Users, MessageSquare, FileText } from 'lucide-react'
|
|
import { Suggestion, SuggestionPriority } from '@/lib/companion/types'
|
|
import { PRIORITY_COLORS } from '@/lib/companion/constants'
|
|
|
|
interface SuggestionListProps {
|
|
suggestions: Suggestion[]
|
|
onSuggestionClick?: (suggestion: Suggestion) => void
|
|
loading?: boolean
|
|
maxItems?: number
|
|
}
|
|
|
|
const iconMap: Record<string, React.ComponentType<{ className?: string }>> = {
|
|
ClipboardCheck,
|
|
BookOpen,
|
|
Calendar,
|
|
Users,
|
|
Clock,
|
|
MessageSquare,
|
|
FileText,
|
|
Lightbulb,
|
|
}
|
|
|
|
function getIcon(iconName: string) {
|
|
const Icon = iconMap[iconName] || Lightbulb
|
|
return Icon
|
|
}
|
|
|
|
interface SuggestionCardProps {
|
|
suggestion: Suggestion
|
|
onClick?: () => void
|
|
}
|
|
|
|
function SuggestionCard({ suggestion, onClick }: SuggestionCardProps) {
|
|
const priorityStyles = PRIORITY_COLORS[suggestion.priority]
|
|
const Icon = getIcon(suggestion.icon)
|
|
|
|
return (
|
|
<button
|
|
onClick={onClick}
|
|
className={`
|
|
w-full p-4 rounded-xl border text-left
|
|
transition-all duration-200
|
|
hover:shadow-md hover:scale-[1.01]
|
|
${priorityStyles.bg} ${priorityStyles.border}
|
|
`}
|
|
>
|
|
<div className="flex items-start gap-3">
|
|
{/* Priority Dot & Icon */}
|
|
<div className="flex-shrink-0 relative">
|
|
<div className={`p-2 rounded-lg bg-white shadow-sm`}>
|
|
<Icon className={`w-5 h-5 ${priorityStyles.text}`} />
|
|
</div>
|
|
<span
|
|
className={`absolute -top-1 -right-1 w-3 h-3 rounded-full ${priorityStyles.dot}`}
|
|
title={`Prioritaet: ${suggestion.priority}`}
|
|
/>
|
|
</div>
|
|
|
|
{/* Content */}
|
|
<div className="flex-1 min-w-0">
|
|
<h4 className={`font-medium ${priorityStyles.text} mb-1`}>
|
|
{suggestion.title}
|
|
</h4>
|
|
<p className="text-sm text-slate-600 line-clamp-2">
|
|
{suggestion.description}
|
|
</p>
|
|
|
|
{/* Meta */}
|
|
<div className="flex items-center gap-3 mt-2">
|
|
<span className="inline-flex items-center gap-1 text-xs text-slate-500">
|
|
<Clock className="w-3 h-3" />
|
|
~{suggestion.estimatedTime} Min
|
|
</span>
|
|
<span className={`text-xs font-medium px-2 py-0.5 rounded-full ${priorityStyles.bg} ${priorityStyles.text}`}>
|
|
{suggestion.priority}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Arrow */}
|
|
<ChevronRight className="w-5 h-5 text-slate-400 flex-shrink-0" />
|
|
</div>
|
|
</button>
|
|
)
|
|
}
|
|
|
|
export function SuggestionList({
|
|
suggestions,
|
|
onSuggestionClick,
|
|
loading,
|
|
maxItems = 5,
|
|
}: SuggestionListProps) {
|
|
// Sort by priority: urgent > high > medium > low
|
|
const priorityOrder: Record<SuggestionPriority, number> = {
|
|
urgent: 0,
|
|
high: 1,
|
|
medium: 2,
|
|
low: 3,
|
|
}
|
|
|
|
const sortedSuggestions = [...suggestions]
|
|
.sort((a, b) => priorityOrder[a.priority] - priorityOrder[b.priority])
|
|
.slice(0, maxItems)
|
|
|
|
if (loading) {
|
|
return (
|
|
<div className="bg-white border border-slate-200 rounded-xl p-6">
|
|
<div className="flex items-center gap-2 mb-4">
|
|
<Lightbulb className="w-5 h-5 text-amber-500" />
|
|
<h3 className="font-semibold text-slate-900">Vorschlaege</h3>
|
|
</div>
|
|
<div className="space-y-3">
|
|
{[1, 2, 3].map((i) => (
|
|
<div key={i} className="h-24 bg-slate-100 rounded-xl animate-pulse" />
|
|
))}
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
if (suggestions.length === 0) {
|
|
return (
|
|
<div className="bg-white border border-slate-200 rounded-xl p-6">
|
|
<div className="flex items-center gap-2 mb-4">
|
|
<Lightbulb className="w-5 h-5 text-amber-500" />
|
|
<h3 className="font-semibold text-slate-900">Vorschlaege</h3>
|
|
</div>
|
|
<div className="text-center py-8">
|
|
<div className="w-12 h-12 bg-green-100 rounded-full flex items-center justify-center mx-auto mb-3">
|
|
<ClipboardCheck className="w-6 h-6 text-green-600" />
|
|
</div>
|
|
<p className="text-slate-600">Alles erledigt!</p>
|
|
<p className="text-sm text-slate-500 mt-1">Keine offenen Aufgaben</p>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<div className="bg-white border border-slate-200 rounded-xl p-6">
|
|
<div className="flex items-center justify-between mb-4">
|
|
<div className="flex items-center gap-2">
|
|
<Lightbulb className="w-5 h-5 text-amber-500" />
|
|
<h3 className="font-semibold text-slate-900">Vorschlaege</h3>
|
|
</div>
|
|
<span className="text-sm text-slate-500">
|
|
{suggestions.length} Aufgabe{suggestions.length !== 1 ? 'n' : ''}
|
|
</span>
|
|
</div>
|
|
|
|
<div className="space-y-3">
|
|
{sortedSuggestions.map((suggestion) => (
|
|
<SuggestionCard
|
|
key={suggestion.id}
|
|
suggestion={suggestion}
|
|
onClick={() => onSuggestionClick?.(suggestion)}
|
|
/>
|
|
))}
|
|
</div>
|
|
|
|
{suggestions.length > maxItems && (
|
|
<button className="w-full mt-4 py-2 text-sm text-slate-600 hover:text-slate-900 hover:bg-slate-50 rounded-lg transition-colors">
|
|
Alle {suggestions.length} anzeigen
|
|
</button>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|