'use client'; import React, { useEffect, useMemo, useState } from 'react'; import { motion } from 'framer-motion'; import { MessageSquare, Plus, Pencil, Trash2, Lock, LockOpen, AlertCircle, FileText, Star, BookOpen, AlertTriangle, Users, Sparkles, } from 'lucide-react'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { Skeleton } from '@/components/ui/skeleton'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from '@/components/ui/dialog'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from '@/components/ui/alert-dialog'; import { Label } from '@/components/ui/label'; import { Textarea } from '@/components/ui/textarea'; import { Checkbox } from '@/components/ui/checkbox'; import { useAppStore } from '@/lib/store'; import { t } from '@/lib/i18n'; import { fetchTeacherNotes, createTeacherNote, updateTeacherNote, deleteTeacherNote, type TeacherNote, type TeacherNoteCategory, } from '@/lib/api'; import { toast } from 'sonner'; const CATEGORIES: TeacherNoteCategory[] = [ 'GENERAL', 'BEHAVIOR', 'ACADEMIC', 'INTERVENTION', 'PARENT_CONTACT', ]; const CATEGORY_META: Record< TeacherNoteCategory, { iconComponent: React.ElementType; color: string; border: string; dot: string; labelKey: string; gradient: string } > = { GENERAL: { iconComponent: FileText, color: 'bg-gray-100 text-gray-700 dark:bg-gray-900/40 dark:text-gray-300', border: 'border-l-gray-400 dark:border-l-gray-600', dot: 'bg-gray-400 dark:bg-gray-500', labelKey: 'teacher_notes.cat_general', gradient: 'from-gray-50 to-transparent dark:from-gray-900/20', }, BEHAVIOR: { iconComponent: Star, color: 'bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-300', border: 'border-l-amber-400 dark:border-l-amber-600', dot: 'bg-amber-400 dark:bg-amber-500', labelKey: 'teacher_notes.cat_behavior', gradient: 'from-amber-50 to-transparent dark:from-amber-900/15', }, ACADEMIC: { iconComponent: BookOpen, color: 'bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-300', border: 'border-l-emerald-400 dark:border-l-emerald-600', dot: 'bg-emerald-400 dark:bg-emerald-500', labelKey: 'teacher_notes.cat_academic', gradient: 'from-emerald-50 to-transparent dark:from-emerald-900/15', }, INTERVENTION: { iconComponent: AlertTriangle, color: 'bg-rose-100 text-rose-700 dark:bg-rose-900/30 dark:text-rose-300', border: 'border-l-rose-400 dark:border-l-rose-600', dot: 'bg-rose-400 dark:bg-rose-500', labelKey: 'teacher_notes.cat_intervention', gradient: 'from-rose-50 to-transparent dark:from-rose-900/15', }, PARENT_CONTACT: { iconComponent: Users, color: 'bg-violet-100 text-violet-700 dark:bg-violet-900/30 dark:text-violet-300', border: 'border-l-violet-400 dark:border-l-violet-600', dot: 'bg-violet-400 dark:bg-violet-500', labelKey: 'teacher_notes.cat_parent_contact', gradient: 'from-violet-50 to-transparent dark:from-violet-900/15', }, }; function relativeTime(iso: string, locale: string): string { const date = new Date(iso); const now = new Date(); const diffMs = now.getTime() - date.getTime(); const sec = Math.floor(diffMs / 1000); const min = Math.floor(sec / 60); const hr = Math.floor(min / 60); const day = Math.floor(hr / 24); const wk = Math.floor(day / 7); const mo = Math.floor(day / 30); const isDe = locale === 'de'; if (sec < 60) return isDe ? 'gerade eben' : 'just now'; if (min < 60) return isDe ? `vor ${min} Min.` : `${min}m ago`; if (hr < 24) return isDe ? `vor ${hr} Std.` : `${hr}h ago`; if (day < 7) return isDe ? `vor ${day} Tg.` : `${day}d ago`; if (wk < 5) return isDe ? `vor ${wk} Wo.` : `${wk}w ago`; if (mo < 12) return isDe ? `vor ${mo} Mo.` : `${mo}mo ago`; return date.toLocaleDateString(isDe ? 'de-DE' : 'en-US'); } interface TeacherNotesSectionProps { studentId: string; } export default function TeacherNotesSection({ studentId }: TeacherNotesSectionProps) { const currentUser = useAppStore((s) => s.currentUser); const locale = useAppStore((s) => s.locale); const [notes, setNotes] = useState([]); const [loading, setLoading] = useState(true); const [dialogOpen, setDialogOpen] = useState(false); const [editingNote, setEditingNote] = useState(null); const [formCategory, setFormCategory] = useState('GENERAL'); const [formContent, setFormContent] = useState(''); const [formPrivate, setFormPrivate] = useState(true); const [saving, setSaving] = useState(false); const [deleteId, setDeleteId] = useState(null); const [filterCategory, setFilterCategory] = useState('ALL'); // Count per category const categoryCounts = useMemo(() => { const counts: Record = { GENERAL: 0, BEHAVIOR: 0, ACADEMIC: 0, INTERVENTION: 0, PARENT_CONTACT: 0, }; for (const n of notes) { counts[n.category] = (counts[n.category] ?? 0) + 1; } return counts; }, [notes]); const filteredNotes = useMemo(() => { if (filterCategory === 'ALL') return notes; return notes.filter((n) => n.category === filterCategory); }, [notes, filterCategory]); const MAX_CHARS = 1000; const formContentLength = formContent.length; const loadNotes = React.useCallback(async () => { setLoading(true); try { const list = await fetchTeacherNotes(studentId); setNotes(list); } catch (err) { const msg = err instanceof Error ? err.message : t('error.generic'); toast.error(msg); } finally { setLoading(false); } }, [studentId]); useEffect(() => { loadNotes(); }, [loadNotes]); const openCreate = () => { setEditingNote(null); setFormCategory('GENERAL'); setFormContent(''); setFormPrivate(true); setDialogOpen(true); }; const openEdit = (note: TeacherNote) => { setEditingNote(note); setFormCategory(note.category); setFormContent(note.content); setFormPrivate(note.isPrivate); setDialogOpen(true); }; const handleSave = async () => { if (!formContent.trim()) { toast.error(t('teacher_notes.error_min_content')); return; } setSaving(true); try { if (editingNote) { const updated = await updateTeacherNote(editingNote.id, { category: formCategory, content: formContent.trim(), isPrivate: formPrivate, }); setNotes((prev) => prev.map((n) => (n.id === editingNote.id ? updated : n)) ); toast.success(t('teacher_notes.updated')); } else { const created = await createTeacherNote({ studentId, category: formCategory, content: formContent.trim(), isPrivate: formPrivate, }); setNotes((prev) => [created, ...prev]); toast.success(t('teacher_notes.created')); } setDialogOpen(false); } catch (err) { const msg = err instanceof Error ? err.message : t('error.generic'); toast.error(msg); } finally { setSaving(false); } }; const handleDelete = async () => { if (!deleteId) return; try { await deleteTeacherNote(deleteId); setNotes((prev) => prev.filter((n) => n.id !== deleteId)); toast.success(t('teacher_notes.deleted')); } catch (err) { const msg = err instanceof Error ? err.message : t('error.generic'); toast.error(msg); } finally { setDeleteId(null); } }; return (
{t('teacher_notes.title')} {notes.length}

{t('teacher_notes.subtitle')}

{loading ? (
) : notes.length === 0 ? (
{/* Illustrated empty state */}
{/* Floating sparkle decoration */}

{t('teacher_notes.no_notes')}

{t('teacher_notes.no_notes_hint')}

) : ( <> {/* Filter chips */}
{CATEGORIES.map((cat) => { const meta = CATEGORY_META[cat]; const count = categoryCounts[cat] ?? 0; if (count === 0) return null; return ( ); })}
{filteredNotes.length === 0 ? (

{t('polish.no_results')}

) : filteredNotes.map((note, i) => { const meta = CATEGORY_META[note.category] ?? CATEGORY_META.GENERAL; const isOwn = note.teacherId === currentUser?.id; return (
{t(meta.labelKey)} {isOwn ? t('teacher_notes.you') : `${note.teacher.firstName} ${note.teacher.lastName}`} ยท {relativeTime(note.createdAt, locale)} {note.updatedAt !== note.createdAt && ( ({t('polish.last_edited')} {relativeTime(note.updatedAt, locale)}) )} {note.isPrivate ? ( ) : ( )}

{note.content}

{isOwn && (
)}
); })}
)} {/* Create / edit dialog */} {editingNote ? t('teacher_notes.edit_title') : t('teacher_notes.add_title')} {t('teacher_notes.subtitle')}
MAX_CHARS ? 'text-rose-600 dark:text-rose-400 font-bold' : formContentLength > MAX_CHARS * 0.8 ? 'text-amber-600 dark:text-amber-400' : 'text-gray-400 dark:text-gray-500' }`}> {formContentLength} / {MAX_CHARS}