import React, { useEffect, useState } from 'react'; import { Check, Plus, Calendar, Trash2, RotateCcw, FileText, LayoutList, Columns, ArrowRight, ArrowLeft, Edit3, X, Layers, Share2, Users, } from 'lucide-react'; import { TaskList, Thought, Space } from '../../lib/types'; import { api, triggerHaptic } from '../../lib/api'; const getInitials = (name?: string): string => { if (!name) return '?'; const parts = name.trim().split(/\s+/); if (parts.length >= 2) { return (parts[0][0] + parts[1][0]).toUpperCase(); } return name.slice(0, 2).toUpperCase(); }; interface TasksViewProps { onRefreshStats: () => void; onOpenDocument: (docId: string) => void; refreshTrigger?: number; } export const TasksView: React.FC = ({ onRefreshStats, onOpenDocument, refreshTrigger = 0, }) => { const [viewMode, setViewMode] = useState<'list' | 'kanban'>('list'); const [filter, setFilter] = useState<'open' | 'done' | 'discarded'>('open'); const [tasks, setTasks] = useState([]); const [taskLists, setTaskLists] = useState([]); const [selectedListId, setSelectedListId] = useState('all'); const [spaces, setSpaces] = useState([]); const [discarded, setDiscarded] = useState([]); const [loading, setLoading] = useState(true); // Quick Task Add state const [quickTitle, setQuickTitle] = useState(''); const [quickDueDate, setQuickDueDate] = useState(''); // New TaskList modal state const [showNewListModal, setShowNewListModal] = useState(false); const [newListTitle, setNewListTitle] = useState(''); const [newListIcon, setNewListIcon] = useState('📋'); const [newListSpaceId, setNewListSpaceId] = useState(''); // Share TaskList modal state const [showShareListModal, setShowShareListModal] = useState(false); const [sharingTaskList, setSharingTaskList] = useState(null); const [shareTargetSpaceId, setShareTargetSpaceId] = useState(''); // Active notes inline edit ID const [activeNotesTaskId, setActiveNotesTaskId] = useState(null); const [notesDraft, setNotesDraft] = useState(''); const loadData = async () => { setLoading(true); try { const [allTasks, allLists, allSpaces, allDiscarded] = await Promise.all([ api.getTasks(selectedListId === 'all' ? undefined : selectedListId).catch(() => []), api.getTaskLists().catch(() => []), api.getSpaces().catch(() => []), api.getThoughts('discarded').catch(() => []), ]); setTasks(Array.isArray(allTasks) ? allTasks : []); setTaskLists(Array.isArray(allLists) ? allLists : []); setSpaces(Array.isArray(allSpaces) ? allSpaces : []); setDiscarded(Array.isArray(allDiscarded) ? allDiscarded : []); } catch { // Prevent unhandled promise rejection from crashing view } finally { setLoading(false); } }; useEffect(() => { loadData(); }, [refreshTrigger, selectedListId]); const handleToggleDone = async (task: Thought) => { triggerHaptic(15); const nextState = !task.isCompleted; const nextKanban = nextState ? 'done' : 'todo'; setTasks((prev) => prev.map((t) => t.id === task.id ? { ...t, isCompleted: nextState, kanbanStatus: nextKanban } : t ) ); await api.updateThought(task.id, { isCompleted: nextState, kanbanStatus: nextKanban }); onRefreshStats(); }; const handleKanbanMove = async (task: Thought, nextStatus: 'todo' | 'in_progress' | 'done') => { triggerHaptic(15); const isCompleted = nextStatus === 'done'; setTasks((prev) => prev.map((t) => (t.id === task.id ? { ...t, kanbanStatus: nextStatus, isCompleted } : t)) ); await api.updateThought(task.id, { kanbanStatus: nextStatus, isCompleted }); onRefreshStats(); }; const handleDueDateChange = async (task: Thought, newDate: string) => { setTasks((prev) => prev.map((t) => (t.id === task.id ? { ...t, dueDate: newDate } : t))); await api.updateThought(task.id, { dueDate: newDate }); }; const handleSaveNotes = async (task: Thought) => { setTasks((prev) => prev.map((t) => (t.id === task.id ? { ...t, notes: notesDraft } : t))); await api.updateThought(task.id, { notes: notesDraft }); setActiveNotesTaskId(null); }; const handleConvertToDoc = async (task: Thought) => { triggerHaptic(20); const updated = await api.triageAction(task.id, 'keep', { convertTo: 'document' }); onRefreshStats(); if (updated.convertedRefId) { onOpenDocument(updated.convertedRefId); } else { loadData(); } }; const handleRestore = async (item: Thought) => { triggerHaptic(15); await api.triageAction(item.id, 'restore'); await loadData(); onRefreshStats(); }; const handleDeletePermanent = async (id: string) => { triggerHaptic(20); await api.deleteThought(id); await loadData(); onRefreshStats(); }; const handlePurgeAllDiscarded = async () => { await api.purgeDiscarded(); await loadData(); onRefreshStats(); }; const handleQuickAddTask = async (e: React.FormEvent) => { e.preventDefault(); const title = quickTitle.trim(); if (!title) return; triggerHaptic(10); setQuickTitle(''); try { const created = await api.captureThought(title, 'normal', 'manual', quickDueDate, { target: 'task', taskListId: selectedListId === 'all' ? '' : selectedListId, }); setTasks((prev) => [created, ...prev]); onRefreshStats(); } catch { loadData(); } }; const handleCreateTaskList = async (e: React.FormEvent) => { e.preventDefault(); const title = newListTitle.trim(); if (!title) return; try { const created = await api.createTaskList({ title, icon: newListIcon, spaceId: newListSpaceId, }); setTaskLists((prev) => [...prev, created]); setSelectedListId(created.id); setShowNewListModal(false); setNewListTitle(''); } catch (err: any) { alert(err.message || 'Fehler beim Erstellen der Aufgabenliste'); } }; const handleDeleteTaskList = async (listId: string) => { if ( !window.confirm( 'Möchten Sie diese Aufgabenliste wirklich löschen? Die enthaltenen Aufgaben bleiben erhalten.' ) ) return; try { await api.deleteTaskList(listId); setTaskLists((prev) => prev.filter((l) => l.id !== listId)); setSelectedListId('all'); } catch (err: any) { alert(err.message); } }; const handleOpenShareModal = (tl: TaskList) => { setSharingTaskList(tl); setShareTargetSpaceId(tl.spaceId || ''); setShowShareListModal(true); }; const handleSaveShareList = async () => { if (!sharingTaskList) return; triggerHaptic(15); try { const updated = await api.updateTaskList(sharingTaskList.id, { spaceId: shareTargetSpaceId, }); setTaskLists((prev) => prev.map((l) => (l.id === updated.id ? { ...l, ...updated } : l)) ); setShowShareListModal(false); setSharingTaskList(null); await loadData(); } catch (err: any) { alert(err.message || 'Fehler beim Speichern der Freigabe'); } }; const safeTasks = Array.isArray(tasks) ? tasks : []; const safeTaskLists = Array.isArray(taskLists) ? taskLists : []; const safeSpaces = Array.isArray(spaces) ? spaces : []; const safeDiscarded = Array.isArray(discarded) ? discarded : []; const openTasks = safeTasks.filter((t) => !t.isCompleted); const doneTasks = safeTasks.filter((t) => t.isCompleted); const todoKanban = safeTasks.filter( (t) => (!t.kanbanStatus || t.kanbanStatus === 'todo') && !t.isCompleted ); const inProgressKanban = safeTasks.filter( (t) => t.kanbanStatus === 'in_progress' && !t.isCompleted ); const doneKanban = safeTasks.filter((t) => t.kanbanStatus === 'done' || t.isCompleted); const currentSelectedList = safeTaskLists.find((l) => l.id === selectedListId); return (
{/* Top Header: Task Lists Tabs & View Mode Switcher */}
{/* Task Lists selector tabs */}
{safeTaskLists.map((tl) => (
{selectedListId === tl.id && ( )}
))}
{/* Right Header Section: Sharing, Avatar Stack & View Switcher */}
{currentSelectedList && (
{/* Avatar Stack in the corner */} {currentSelectedList.members && currentSelectedList.members.length > 0 ? (
handleOpenShareModal(currentSelectedList)} className="flex items-center -space-x-1.5 cursor-pointer hover:opacity-90 transition-opacity pl-0.5" title={`Geteilt mit ${currentSelectedList.members.length} Mitgliedern (${currentSelectedList.spaceName || 'Team'}). Klick zum Verwalten.`} > {currentSelectedList.members.slice(0, 3).map((m) => (
{getInitials(m.displayName || m.email)}
))} {currentSelectedList.members.length > 3 && (
+{currentSelectedList.members.length - 3}
)}
) : (
handleOpenShareModal(currentSelectedList)} className="flex items-center cursor-pointer pl-0.5" title="Privat (nur du). Klick zum Teilen." >
đź”’
)}
)} {/* View Switcher: List vs Kanban */}
{/* Quick Add Task Input in active list */}
setQuickTitle(e.target.value)} placeholder={ currentSelectedList ? `Aufgabe zu "${currentSelectedList.title}" hinzufügen… (Enter)` : 'Neue Aufgabe anlegen… (Enter)' } className="flex-1 bg-transparent px-2 py-1 text-sm text-main placeholder-faint focus:outline-none" />
setQuickDueDate(e.target.value)} className="h-7 px-2 input-field text-xs" title="Fälligkeitsdatum" />
{/* NEW LIST MODAL */} {showNewListModal && (

Neue Aufgabenliste erstellen

setNewListIcon(e.target.value)} maxLength={2} className="w-10 h-8 text-center input-field text-sm" title="Icon (Emoji)" /> setNewListTitle(e.target.value)} placeholder="z.B. Homelab, Einkäufe, Steuern" autoFocus required className="flex-1 h-8 input-field px-2.5 text-xs" />
{safeSpaces.length > 0 && (
)}
)} {/* SHARE LIST MODAL */} {showShareListModal && sharingTaskList && (

Liste teilen: {sharingTaskList.title}

{/* Members preview */} {(() => { const selectedSpace = safeSpaces.find((s) => s.id === shareTargetSpaceId); const members = selectedSpace?.members || []; if (members.length > 0) { return (

Personen mit Zugriff ({members.length}):

{members.map((m) => (
{getInitials(m.displayName || m.email)}

{m.displayName || m.email}

{m.email}

{m.role === 'owner' ? 'EigentĂĽmer' : m.role === 'editor' ? 'Bearbeiter' : 'Mitglied'}
))}
); } else if (shareTargetSpaceId) { return (

Keine Mitglieder in diesem Bereich.

); } else { return (

Diese Aufgabenliste ist privat.

Wähle oben einen Familien- oder Team-Space aus, um die Liste und alle enthaltenen Aufgaben automatisch mit den Mitgliedern zu teilen.

); } })()}
)} {/* VIEW MODES: LIST OR KANBAN */} {viewMode === 'kanban' ? ( /* KANBAN BOARD (3 COLUMNS) */
{/* COLUMN 1: TODO */}
Zu erledigen {todoKanban.length}
{todoKanban.map((task) => (

{task.content}

{/* Notes Preview / Inline Editor */} {activeNotesTaskId === task.id ? (