Initial commit: breakpilot-lehrer - Lehrer KI Platform
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>
This commit is contained in:
@@ -0,0 +1,170 @@
|
||||
'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>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user