feat: two-column priority layout + German ABCDE translations

All four priority modes (Eisenhower, ABCDE, Ivy Lee, Pareto) now use a
two-column layout on desktop: main content on the left, secondary panel
(unassigned tasks / pool / remaining) on the right. On mobile (<768px)
the layout stacks vertically automatically.

Eisenhower: filters are now always visible in the right panel instead
of behind a toggle button, making project/list/timespan selection more
accessible. The filter button is hidden for Eisenhower mode.

ABCDE: added full German translations for grade labels and descriptions
(A–E now show "Kritisch", "Sollte erledigt werden", etc. in German).
Ungraded tasks appear in the right column instead of below the buckets.

Pareto: remaining tasks (80%) are always visible in the right column,
no longer behind a show/hide toggle.

v1.90.0
This commit is contained in:
mARTin 2026-04-06 20:48:03 +02:00
parent 0a84bf3114
commit 3ee5bf33b5
2 changed files with 439 additions and 315 deletions

View File

@ -1,6 +1,6 @@
{
"name": "my-weekly-todo-list",
"version": "1.89.0",
"version": "1.90.0",
"description": "A web-based weekly task management application that organizes to-dos and calendar events in a single, intuitive weekly view",
"main": "index.js",
"scripts": {

View File

@ -1,6 +1,6 @@
"use client";
import React, { useState, useMemo, useCallback } from "react";
import React, { useState, useMemo, useCallback, useEffect } from "react";
import {
AlertTriangle,
Target,
@ -88,6 +88,14 @@ const ABCDE_LABELS_DARK: Record<string, { bg: string }> = {
E: { bg: "#1f2937" },
};
const ABCDE_LABELS_DE: Record<string, { label: string; desc: string }> = {
A: { label: "A — Kritisch", desc: "Muss erledigt werden. Ernste Konsequenzen." },
B: { label: "B — Sollte erledigt werden", desc: "Leichte Konsequenzen wenn nicht erledigt." },
C: { label: "C — Schön wenn erledigt", desc: "Keine Konsequenzen wenn nicht erledigt." },
D: { label: "D — Delegieren", desc: "Kann von jemand anderem erledigt werden." },
E: { label: "E — Eliminieren", desc: "Streichen — kein echter Mehrwert." },
};
function today(): string {
return new Date().toISOString().split("T")[0];
}
@ -129,9 +137,17 @@ export default function PriorityView({
const [delegateType, setDelegateType] = useState<"person" | "ai">("person");
const [ivyLeeSelected, setIvyLeeSelected] = useState<Set<string>>(new Set());
const [showFilters, setShowFilters] = useState(false);
const [isMobile, setIsMobile] = useState(false);
const de = language === "de";
useEffect(() => {
const check = () => setIsMobile(window.innerWidth < 768);
check();
window.addEventListener("resize", check);
return () => window.removeEventListener("resize", check);
}, []);
// --- Filter tasks ---
const filteredTasks = useMemo(() => {
let result = tasks.filter((t) => !t.id.startsWith("virtual-"));
@ -258,97 +274,108 @@ export default function PriorityView({
const accentColor = "#6366f1";
const methodButtons: { key: PriorityMethod; label: string; icon: React.ReactNode }[] = [
{ key: "eisenhower", label: de ? "Eisenhower" : "Eisenhower", icon: <Grid size={14} /> },
{ key: "eisenhower", label: "Eisenhower", icon: <Grid size={14} /> },
{ key: "abcde", label: "ABCDE", icon: <Tag size={14} /> },
{ key: "ivylee", label: de ? "Ivy Lee" : "Ivy Lee", icon: <ListOrdered size={14} /> },
{ key: "pareto", label: de ? "80/20" : "80/20", icon: <BarChart2 size={14} /> },
{ key: "ivylee", label: "Ivy Lee", icon: <ListOrdered size={14} /> },
{ key: "pareto", label: "80/20", icon: <BarChart2 size={14} /> },
];
// Filter panel — rendered in Eisenhower's right column or above content in other modes
const filterPanel = (
<div style={{ background: cardBg, border: `1px solid ${border}`, borderRadius: "10px", padding: "10px", display: "flex", flexWrap: "wrap", gap: "8px" }}>
<div style={{ display: "flex", flexDirection: "column", gap: "3px", flex: "1 1 120px" }}>
<label style={{ fontSize: "0.68rem", fontWeight: 600, color: textSecondary, textTransform: "uppercase", letterSpacing: "0.05em" }}>
<FolderOpen size={9} style={{ display: "inline", marginRight: "3px" }} />
{de ? "Projekt" : "Project"}
</label>
<select
value={filterProject}
onChange={(e) => setFilterProject(e.target.value)}
style={{ padding: "4px 7px", borderRadius: "6px", border: `1px solid ${border}`, background: darkMode ? "#374151" : "#f3f4f6", color: textPrimary, fontSize: "0.8rem", cursor: "pointer" }}
>
<option value="">{de ? "Alle Projekte" : "All projects"}</option>
{projects.map((p) => (
<option key={p.id} value={p.id}>{p.name}</option>
))}
</select>
</div>
<div style={{ display: "flex", flexDirection: "column", gap: "3px", flex: "1 1 130px" }}>
<label style={{ fontSize: "0.68rem", fontWeight: 600, color: textSecondary, textTransform: "uppercase", letterSpacing: "0.05em" }}>
<List size={9} style={{ display: "inline", marginRight: "3px" }} />
{de ? "Liste" : "List"}
</label>
<select
value={filterList}
onChange={(e) => setFilterList(e.target.value)}
style={{ padding: "4px 7px", borderRadius: "6px", border: `1px solid ${border}`, background: darkMode ? "#374151" : "#f3f4f6", color: textPrimary, fontSize: "0.8rem", cursor: "pointer" }}
>
<option value="">{de ? "Alle Listen" : "All lists"}</option>
<option value="__scheduled__">{de ? "Geplante Aufgaben" : "Scheduled"}</option>
<option value="__unscheduled__">{de ? "Ungeplant" : "Unscheduled"}</option>
{somedayLists.map((l) => (
<option key={l.id} value={l.id}>{l.title}</option>
))}
</select>
</div>
<div style={{ display: "flex", flexDirection: "column", gap: "3px", flex: "1 1 120px" }}>
<label style={{ fontSize: "0.68rem", fontWeight: 600, color: textSecondary, textTransform: "uppercase", letterSpacing: "0.05em" }}>
<CalendarDays size={9} style={{ display: "inline", marginRight: "3px" }} />
{de ? "Zeitraum" : "Timespan"}
</label>
<select
value={filterTimespan}
onChange={(e) => setFilterTimespan(e.target.value)}
style={{ padding: "4px 7px", borderRadius: "6px", border: `1px solid ${border}`, background: darkMode ? "#374151" : "#f3f4f6", color: textPrimary, fontSize: "0.8rem", cursor: "pointer" }}
>
<option value="all">{de ? "Alle" : "All time"}</option>
<option value="today">{de ? "Heute" : "Today"}</option>
<option value="this-week">{de ? "Diese Woche" : "This week"}</option>
<option value="next-week">{de ? "Nächste Woche" : "Next week"}</option>
<option value="unscheduled">{de ? "Ungeplant" : "Unscheduled"}</option>
</select>
</div>
<div style={{ display: "flex", alignItems: "flex-end", paddingBottom: "2px" }}>
<span style={{ fontSize: "0.75rem", color: textSecondary }}>
{filteredTasks.length} {de ? "Aufgaben" : "tasks"}
</span>
</div>
</div>
);
return (
<div style={{ background: bg, minHeight: "100%", padding: "16px 12px", boxSizing: "border-box", fontFamily: "inherit" }}>
<div style={{ background: bg, minHeight: "100%", padding: "12px", boxSizing: "border-box", fontFamily: "inherit" }}>
{/* ─── Header ─── */}
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: "16px", flexWrap: "wrap", gap: "8px" }}>
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: "10px", flexWrap: "wrap", gap: "8px" }}>
<div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
<Target size={20} style={{ color: accentColor }} />
<span style={{ fontSize: "1.1rem", fontWeight: 700, color: textPrimary }}>
<Target size={18} style={{ color: accentColor }} />
<span style={{ fontSize: "1rem", fontWeight: 700, color: textPrimary }}>
{de ? "Prioritäten" : "Priority View"}
</span>
</div>
<div style={{ display: "flex", gap: "6px", alignItems: "center" }}>
{/* Filter toggle — only for non-Eisenhower modes (Eisenhower shows filters in its right panel) */}
{method !== "eisenhower" && (
<button
onClick={() => setShowFilters((v) => !v)}
style={{ display: "flex", alignItems: "center", gap: "4px", padding: "5px 10px", borderRadius: "7px", border: `1px solid ${border}`, background: showFilters ? accentColor : cardBg, color: showFilters ? "#fff" : textSecondary, cursor: "pointer", fontSize: "0.8rem" }}
>
<SlidersHorizontal size={13} />
{de ? "Filter" : "Filter"}
{(filterProject || filterList || filterTimespan !== "all") && (
<span style={{ background: "#ef4444", color: "#fff", borderRadius: "99px", fontSize: "0.65rem", padding: "0 5px", fontWeight: 700 }}>!</span>
)}
</button>
</div>
)}
</div>
{/* ─── Filters Panel ─── */}
{showFilters && (
<div style={{ background: cardBg, border: `1px solid ${border}`, borderRadius: "10px", padding: "12px", marginBottom: "16px", display: "flex", flexWrap: "wrap", gap: "10px" }}>
<div style={{ display: "flex", flexDirection: "column", gap: "4px", minWidth: "140px" }}>
<label style={{ fontSize: "0.7rem", fontWeight: 600, color: textSecondary, textTransform: "uppercase", letterSpacing: "0.05em" }}>
<FolderOpen size={10} style={{ display: "inline", marginRight: "3px" }} />
{de ? "Projekt" : "Project"}
</label>
<select
value={filterProject}
onChange={(e) => setFilterProject(e.target.value)}
style={{ padding: "5px 8px", borderRadius: "6px", border: `1px solid ${border}`, background: darkMode ? "#374151" : "#f3f4f6", color: textPrimary, fontSize: "0.82rem", cursor: "pointer" }}
>
<option value="">{de ? "Alle Projekte" : "All projects"}</option>
{projects.map((p) => (
<option key={p.id} value={p.id}>{p.name}</option>
))}
</select>
</div>
<div style={{ display: "flex", flexDirection: "column", gap: "4px", minWidth: "160px" }}>
<label style={{ fontSize: "0.7rem", fontWeight: 600, color: textSecondary, textTransform: "uppercase", letterSpacing: "0.05em" }}>
<List size={10} style={{ display: "inline", marginRight: "3px" }} />
{de ? "Liste" : "List"}
</label>
<select
value={filterList}
onChange={(e) => setFilterList(e.target.value)}
style={{ padding: "5px 8px", borderRadius: "6px", border: `1px solid ${border}`, background: darkMode ? "#374151" : "#f3f4f6", color: textPrimary, fontSize: "0.82rem", cursor: "pointer" }}
>
<option value="">{de ? "Alle Listen" : "All lists"}</option>
<option value="__scheduled__">{de ? "Geplante Aufgaben" : "Scheduled tasks"}</option>
<option value="__unscheduled__">{de ? "Ungeplant" : "Unscheduled"}</option>
{somedayLists.map((l) => (
<option key={l.id} value={l.id}>{l.title}</option>
))}
</select>
</div>
<div style={{ display: "flex", flexDirection: "column", gap: "4px", minWidth: "140px" }}>
<label style={{ fontSize: "0.7rem", fontWeight: 600, color: textSecondary, textTransform: "uppercase", letterSpacing: "0.05em" }}>
<CalendarDays size={10} style={{ display: "inline", marginRight: "3px" }} />
{de ? "Zeitraum" : "Timespan"}
</label>
<select
value={filterTimespan}
onChange={(e) => setFilterTimespan(e.target.value)}
style={{ padding: "5px 8px", borderRadius: "6px", border: `1px solid ${border}`, background: darkMode ? "#374151" : "#f3f4f6", color: textPrimary, fontSize: "0.82rem", cursor: "pointer" }}
>
<option value="all">{de ? "Alle" : "All time"}</option>
<option value="today">{de ? "Heute" : "Today"}</option>
<option value="this-week">{de ? "Diese Woche" : "This week"}</option>
<option value="next-week">{de ? "Nächste Woche" : "Next week"}</option>
<option value="unscheduled">{de ? "Ungeplant" : "Unscheduled"}</option>
</select>
</div>
<div style={{ display: "flex", alignItems: "flex-end" }}>
<span style={{ fontSize: "0.78rem", color: textSecondary, paddingBottom: "6px" }}>
{filteredTasks.length} {de ? "Aufgaben" : "tasks"}
</span>
</div>
{/* ─── Filters Panel (non-Eisenhower modes only) ─── */}
{method !== "eisenhower" && showFilters && (
<div style={{ marginBottom: "12px" }}>
{filterPanel}
</div>
)}
{/* ─── Method Tabs ─── */}
<div style={{ display: "flex", gap: "4px", marginBottom: "20px", background: darkMode ? "#1f2937" : "#e5e7eb", borderRadius: "9px", padding: "3px" }}>
<div style={{ display: "flex", gap: "4px", marginBottom: "14px", background: darkMode ? "#1f2937" : "#e5e7eb", borderRadius: "9px", padding: "3px" }}>
{methodButtons.map((m) => (
<button
key={m.key}
@ -398,6 +425,8 @@ export default function PriorityView({
border={border}
textPrimary={textPrimary}
textSecondary={textSecondary}
isMobile={isMobile}
filterPanel={filterPanel}
/>
)}
@ -417,6 +446,7 @@ export default function PriorityView({
border={border}
textPrimary={textPrimary}
textSecondary={textSecondary}
isMobile={isMobile}
/>
)}
@ -436,6 +466,7 @@ export default function PriorityView({
border={border}
textPrimary={textPrimary}
textSecondary={textSecondary}
isMobile={isMobile}
/>
)}
@ -454,6 +485,7 @@ export default function PriorityView({
border={border}
textPrimary={textPrimary}
textSecondary={textSecondary}
isMobile={isMobile}
/>
)}
@ -760,16 +792,18 @@ interface EisenhowerProps {
border: string;
textPrimary: string;
textSecondary: string;
isMobile?: boolean;
filterPanel?: React.ReactNode;
}
const QUADRANT_CONFIG = [
{ urgency: true, importance: true, label: "Do", labelDe: "Sofort erledigen", desc: "Urgent & Important", descDe: "Dringend & Wichtig", color: "#ef4444", bgLight: "#fef2f2", bgDark: "#450a0a", icon: <AlertTriangle size={14} /> },
{ urgency: false, importance: true, label: "Schedule", labelDe: "Planen", desc: "Important, Not Urgent", descDe: "Wichtig, nicht dringend", color: "#3b82f6", bgLight: "#eff6ff", bgDark: "#1e3a5f", icon: <Clock size={14} /> },
{ urgency: true, importance: false, label: "Delegate", labelDe: "Delegieren", desc: "Urgent, Not Important", descDe: "Dringend, nicht wichtig", color: "#f97316", bgLight: "#fff7ed", bgDark: "#431407", icon: <User size={14} /> },
{ urgency: false, importance: false, label: "Delete", labelDe: "Eliminieren", desc: "Neither", descDe: "Weder noch", color: "#6b7280", bgLight: "#f9fafb", bgDark: "#1f2937", icon: <Trash2 size={14} /> },
{ urgency: true, importance: true, label: "Do", labelDe: "Sofort erledigen", desc: "Urgent & Important", descDe: "Dringend & Wichtig", color: "#ef4444", bgLight: "#fef2f2", bgDark: "#450a0a", icon: <AlertTriangle size={14} /> },
{ urgency: false, importance: true, label: "Schedule", labelDe: "Planen", desc: "Important, Not Urgent", descDe: "Wichtig, nicht dringend", color: "#3b82f6", bgLight: "#eff6ff", bgDark: "#1e3a5f", icon: <Clock size={14} /> },
{ urgency: true, importance: false, label: "Delegate", labelDe: "Delegieren", desc: "Urgent, Not Important", descDe: "Dringend, nicht wichtig", color: "#f97316", bgLight: "#fff7ed", bgDark: "#431407", icon: <User size={14} /> },
{ urgency: false, importance: false, label: "Delete", labelDe: "Eliminieren", desc: "Neither", descDe: "Weder noch", color: "#6b7280", bgLight: "#f9fafb", bgDark: "#1f2937", icon: <Trash2 size={14} /> },
];
function EisenhowerMatrix({ q1, q2, q3, q4, unset, allTasks, darkMode, de, onSetQuadrant, onClear, onDelegate, onToggleComplete, onUpdateTask, projects, cardBg, border, textPrimary, textSecondary }: EisenhowerProps) {
function EisenhowerMatrix({ q1, q2, q3, q4, unset, allTasks, darkMode, de, onSetQuadrant, onClear, onDelegate, onToggleComplete, onUpdateTask, projects, cardBg, border, textPrimary, textSecondary, isMobile, filterPanel }: EisenhowerProps) {
const quadrantTasks = [q1, q2, q3, q4];
const [collapsed, setCollapsed] = useState<Record<number, boolean>>({});
const [dragOver, setDragOver] = useState<number | null>(null);
@ -782,77 +816,85 @@ function EisenhowerMatrix({ q1, q2, q3, q4, unset, allTasks, darkMode, de, onSet
setDragOver(null);
};
return (
<div>
{/* Axis labels */}
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: "12px", marginBottom: "12px" }}>
{QUADRANT_CONFIG.map((qc, idx) => {
const tasks = quadrantTasks[idx];
const isCollapsed = collapsed[idx];
const isOver = dragOver === idx;
const quadBg = darkMode ? qc.bgDark : qc.bgLight;
return (
// 2×2 quadrant grid
const quadrantGrid = (
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: "10px" }}>
{QUADRANT_CONFIG.map((qc, idx) => {
const tasks = quadrantTasks[idx];
const isCollapsed = collapsed[idx];
const isOver = dragOver === idx;
const quadBg = darkMode ? qc.bgDark : qc.bgLight;
return (
<div
key={idx}
onDragOver={(e) => { e.preventDefault(); e.dataTransfer.dropEffect = "move"; setDragOver(idx); }}
onDragLeave={(e) => { if (!e.currentTarget.contains(e.relatedTarget as Node)) setDragOver(null); }}
onDrop={(e) => handleQuadrantDrop(e, qc)}
style={{
background: isOver ? `${qc.color}30` : quadBg,
borderRadius: "10px",
border: isOver ? `2px dashed ${qc.color}` : `1px solid ${border}`,
overflow: "hidden",
transition: "border 0.15s, background 0.15s",
}}
>
{/* Quadrant header */}
<div
key={idx}
onDragOver={(e) => { e.preventDefault(); e.dataTransfer.dropEffect = "move"; setDragOver(idx); }}
onDragLeave={(e) => { if (!e.currentTarget.contains(e.relatedTarget as Node)) setDragOver(null); }}
onDrop={(e) => handleQuadrantDrop(e, qc)}
style={{
background: isOver ? `${qc.color}30` : quadBg,
borderRadius: "10px",
border: isOver ? `2px dashed ${qc.color}` : `1px solid ${border}`,
overflow: "hidden",
transition: "border 0.15s, background 0.15s",
}}
style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "8px 12px", cursor: "pointer", borderBottom: isCollapsed ? "none" : `1px solid ${border}` }}
onClick={() => setCollapsed((p) => ({ ...p, [idx]: !p[idx] }))}
>
{/* Quadrant header */}
<div
style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "8px 12px", cursor: "pointer", borderBottom: isCollapsed ? "none" : `1px solid ${border}` }}
onClick={() => setCollapsed((p) => ({ ...p, [idx]: !p[idx] }))}
>
<div style={{ display: "flex", alignItems: "center", gap: "6px" }}>
<span style={{ color: qc.color }}>{qc.icon}</span>
<div>
<div style={{ fontSize: "0.82rem", fontWeight: 700, color: qc.color }}>{de ? qc.labelDe : qc.label}</div>
<div style={{ fontSize: "0.68rem", color: textSecondary }}>{de ? qc.descDe : qc.desc}</div>
</div>
</div>
<div style={{ display: "flex", alignItems: "center", gap: "6px" }}>
<span style={{ fontSize: "0.75rem", fontWeight: 600, color: qc.color, background: `${qc.color}22`, borderRadius: "9px", padding: "1px 7px" }}>{tasks.length}</span>
{isCollapsed ? <ChevronDown size={14} style={{ color: textSecondary }} /> : <ChevronUp size={14} style={{ color: textSecondary }} />}
<div style={{ display: "flex", alignItems: "center", gap: "6px" }}>
<span style={{ color: qc.color }}>{qc.icon}</span>
<div>
<div style={{ fontSize: "0.82rem", fontWeight: 700, color: qc.color }}>{de ? qc.labelDe : qc.label}</div>
<div style={{ fontSize: "0.68rem", color: textSecondary }}>{de ? qc.descDe : qc.desc}</div>
</div>
</div>
{/* Tasks */}
{!isCollapsed && (
<div style={{ padding: "8px", display: "flex", flexDirection: "column", gap: "6px", maxHeight: "260px", overflowY: "auto", minHeight: "44px" }}>
{tasks.length === 0 && (
<p style={{ fontSize: "0.75rem", color: isOver ? qc.color : textSecondary, textAlign: "center", padding: "8px 0", margin: 0 }}>
{isOver ? (de ? "Hier ablegen" : "Drop here") : "—"}
</p>
)}
{tasks.map((t) => (
<TaskCard key={t.id} task={t} darkMode={darkMode} cardBg={cardBg} border={border} textPrimary={textPrimary} textSecondary={textSecondary} onDelegate={onDelegate} onToggleComplete={onToggleComplete} onUpdateTask={onUpdateTask} projects={projects} onRemove={() => onClear(t)} de={de} />
))}
</div>
)}
{/* Collapsed drop hint */}
{isCollapsed && isOver && (
<div style={{ padding: "6px 12px", fontSize: "0.72rem", color: qc.color, textAlign: "center" }}>
{de ? "Hier ablegen" : "Drop here"}
</div>
)}
<div style={{ display: "flex", alignItems: "center", gap: "6px" }}>
<span style={{ fontSize: "0.75rem", fontWeight: 600, color: qc.color, background: `${qc.color}22`, borderRadius: "9px", padding: "1px 7px" }}>{tasks.length}</span>
{isCollapsed ? <ChevronDown size={14} style={{ color: textSecondary }} /> : <ChevronUp size={14} style={{ color: textSecondary }} />}
</div>
</div>
);
})}
</div>
{/* Tasks */}
{!isCollapsed && (
<div style={{ padding: "8px", display: "flex", flexDirection: "column", gap: "6px", maxHeight: "260px", overflowY: "auto", minHeight: "44px" }}>
{tasks.length === 0 && (
<p style={{ fontSize: "0.75rem", color: isOver ? qc.color : textSecondary, textAlign: "center", padding: "8px 0", margin: 0 }}>
{isOver ? (de ? "Hier ablegen" : "Drop here") : "—"}
</p>
)}
{tasks.map((t) => (
<TaskCard key={t.id} task={t} darkMode={darkMode} cardBg={cardBg} border={border} textPrimary={textPrimary} textSecondary={textSecondary} onDelegate={onDelegate} onToggleComplete={onToggleComplete} onUpdateTask={onUpdateTask} projects={projects} onRemove={() => onClear(t)} de={de} />
))}
</div>
)}
{/* Collapsed drop hint */}
{isCollapsed && isOver && (
<div style={{ padding: "6px 12px", fontSize: "0.72rem", color: qc.color, textAlign: "center" }}>
{de ? "Hier ablegen" : "Drop here"}
</div>
)}
</div>
);
})}
</div>
);
// Right panel: filters + unassigned tasks
const rightPanel = (
<div style={{ display: "flex", flexDirection: "column", gap: "10px" }}>
{/* Filters always visible in Eisenhower right column */}
{filterPanel}
{/* Unassigned tasks */}
{unset.length > 0 && (
<div style={{ background: darkMode ? "#1f2937" : "#f3f4f6", borderRadius: "10px", border: `1px solid ${border}`, padding: "12px" }}>
<p style={{ fontSize: "0.78rem", fontWeight: 600, color: textSecondary, marginBottom: "10px" }}>
{de ? `${unset.length} nicht eingestufte Aufgaben — drag in ein Quadrant oder klicke:` : `${unset.length} unclassified tasks — drag into a quadrant or click to assign:`}
</p>
<div style={{ display: "flex", flexDirection: "column", gap: "8px" }}>
<div style={{ background: darkMode ? "#1f2937" : "#f3f4f6", borderRadius: "10px", border: `1px solid ${border}`, padding: "12px" }}>
<p style={{ fontSize: "0.78rem", fontWeight: 600, color: textSecondary, marginBottom: unset.length > 0 ? "10px" : 0, margin: unset.length > 0 ? undefined : 0 }}>
{unset.length > 0
? (de ? `${unset.length} nicht eingestufte Aufgaben — in Quadrant ziehen oder klicken:` : `${unset.length} unclassified — drag to a quadrant or click to assign:`)
: (de ? "Alle Aufgaben sind eingestuft ✓" : "All tasks classified ✓")}
</p>
{unset.length > 0 && (
<div style={{ display: "flex", flexDirection: "column", gap: "8px", maxHeight: "400px", overflowY: "auto" }}>
{unset.map((t) => (
<div key={t.id}>
<TaskCard task={t} darkMode={darkMode} cardBg={cardBg} border={border} textPrimary={textPrimary} textSecondary={textSecondary} onDelegate={onDelegate} onToggleComplete={onToggleComplete} onUpdateTask={onUpdateTask} projects={projects} de={de}>
@ -871,8 +913,26 @@ function EisenhowerMatrix({ q1, q2, q3, q4, unset, allTasks, darkMode, de, onSet
</div>
))}
</div>
</div>
)}
)}
</div>
</div>
);
// Mobile: stack vertically
if (isMobile) {
return (
<div>
{quadrantGrid}
<div style={{ marginTop: "12px" }}>{rightPanel}</div>
</div>
);
}
// Desktop: quadrants left (60%), filter+unassigned right (40%)
return (
<div style={{ display: "flex", gap: "14px", alignItems: "flex-start" }}>
<div style={{ flex: 3, minWidth: 0 }}>{quadrantGrid}</div>
<div style={{ flex: 2, minWidth: "220px", maxWidth: "400px" }}>{rightPanel}</div>
</div>
);
}
@ -894,9 +954,10 @@ interface AbcdeViewProps {
border: string;
textPrimary: string;
textSecondary: string;
isMobile?: boolean;
}
function AbcdeView({ groups, allTasks, darkMode, de, onSetGrade, onDelegate, onToggleComplete, onUpdateTask, projects, cardBg, border, textPrimary, textSecondary }: AbcdeViewProps) {
function AbcdeView({ groups, allTasks, darkMode, de, onSetGrade, onDelegate, onToggleComplete, onUpdateTask, projects, cardBg, border, textPrimary, textSecondary, isMobile }: AbcdeViewProps) {
const [collapsed, setCollapsed] = useState<Record<string, boolean>>({ none: true });
const [dragOver, setDragOver] = useState<string | null>(null);
@ -910,10 +971,12 @@ function AbcdeView({ groups, allTasks, darkMode, de, onSetGrade, onDelegate, onT
setDragOver(null);
};
return (
// AE grade buckets
const gradeBuckets = (
<div style={{ display: "flex", flexDirection: "column", gap: "10px" }}>
{gradeList.map((grade) => {
const cfg = ABCDE_LABELS[grade];
const cfgDe = ABCDE_LABELS_DE[grade];
const cfgDark = ABCDE_LABELS_DARK[grade];
const tasks = groups[grade] || [];
const isCollapsed = collapsed[grade];
@ -940,8 +1003,8 @@ function AbcdeView({ groups, allTasks, darkMode, de, onSetGrade, onDelegate, onT
<div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
<span style={{ fontWeight: 800, fontSize: "1.1rem", color: cfg.color, lineHeight: 1 }}>{grade}</span>
<div>
<div style={{ fontSize: "0.82rem", fontWeight: 700, color: cfg.color }}>{cfg.label}</div>
<div style={{ fontSize: "0.68rem", color: textSecondary }}>{cfg.desc}</div>
<div style={{ fontSize: "0.82rem", fontWeight: 700, color: cfg.color }}>{de ? cfgDe.label : cfg.label}</div>
<div style={{ fontSize: "0.68rem", color: textSecondary }}>{de ? cfgDe.desc : cfg.desc}</div>
</div>
</div>
<div style={{ display: "flex", alignItems: "center", gap: "6px" }}>
@ -969,42 +1032,61 @@ function AbcdeView({ groups, allTasks, darkMode, de, onSetGrade, onDelegate, onT
</div>
);
})}
</div>
);
{/* Ungraded */}
{(groups.none || []).length > 0 && (
<div style={{ background: darkMode ? "#1f2937" : "#f3f4f6", borderRadius: "10px", border: `1px solid ${border}`, overflow: "hidden" }}>
<div
style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "9px 14px", cursor: "pointer", borderBottom: collapsed.none ? "none" : `1px solid ${border}` }}
onClick={() => setCollapsed((p) => ({ ...p, none: !p.none }))}
>
<span style={{ fontSize: "0.82rem", fontWeight: 600, color: textSecondary }}>
{de ? "Nicht eingestuft" : "Ungraded"} ({(groups.none || []).length})
</span>
{collapsed.none ? <ChevronDown size={14} style={{ color: textSecondary }} /> : <ChevronUp size={14} style={{ color: textSecondary }} />}
</div>
{!collapsed.none && (
<div style={{ padding: "10px", display: "flex", flexDirection: "column", gap: "6px" }}>
{(groups.none || []).map((t) => (
<TaskCard key={t.id} task={t} darkMode={darkMode} cardBg={cardBg} border={border} textPrimary={textPrimary} textSecondary={textSecondary} onDelegate={onDelegate} onToggleComplete={onToggleComplete} onUpdateTask={onUpdateTask} projects={projects} de={de}>
<div style={{ display: "flex", gap: "3px", marginTop: "5px", flexWrap: "wrap" }}>
{gradeList.map((g) => (
<button
key={g}
onClick={() => onSetGrade(t, g)}
style={{ fontSize: "0.65rem", padding: "1px 6px", borderRadius: "4px", border: `1px solid ${ABCDE_LABELS[g].color}`, background: `${ABCDE_LABELS[g].color}18`, color: ABCDE_LABELS[g].color, cursor: "pointer", fontWeight: 700 }}
>
{g}
</button>
))}
</div>
</TaskCard>
))}
</div>
)}
// Ungraded tasks panel (right column)
const hasUngraded = (groups.none || []).length > 0;
const ungradedPanel = (
<div style={{ background: darkMode ? "#1f2937" : "#f3f4f6", borderRadius: "10px", border: `1px solid ${border}`, overflow: "hidden" }}>
<div
style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "9px 14px", cursor: "pointer", borderBottom: collapsed.none ? "none" : `1px solid ${border}` }}
onClick={() => setCollapsed((p) => ({ ...p, none: !p.none }))}
>
<span style={{ fontSize: "0.82rem", fontWeight: 600, color: textSecondary }}>
{hasUngraded
? `${de ? "Nicht eingestuft" : "Ungraded"} (${(groups.none || []).length})`
: (de ? "Alle Aufgaben sind eingestuft ✓" : "All tasks graded ✓")}
</span>
{hasUngraded && (collapsed.none ? <ChevronDown size={14} style={{ color: textSecondary }} /> : <ChevronUp size={14} style={{ color: textSecondary }} />)}
</div>
{!collapsed.none && hasUngraded && (
<div style={{ padding: "10px", display: "flex", flexDirection: "column", gap: "6px", maxHeight: "500px", overflowY: "auto" }}>
{(groups.none || []).map((t) => (
<TaskCard key={t.id} task={t} darkMode={darkMode} cardBg={cardBg} border={border} textPrimary={textPrimary} textSecondary={textSecondary} onDelegate={onDelegate} onToggleComplete={onToggleComplete} onUpdateTask={onUpdateTask} projects={projects} de={de}>
<div style={{ display: "flex", gap: "3px", marginTop: "5px", flexWrap: "wrap" }}>
{gradeList.map((g) => (
<button
key={g}
onClick={() => onSetGrade(t, g)}
style={{ fontSize: "0.65rem", padding: "1px 6px", borderRadius: "4px", border: `1px solid ${ABCDE_LABELS[g].color}`, background: `${ABCDE_LABELS[g].color}18`, color: ABCDE_LABELS[g].color, cursor: "pointer", fontWeight: 700 }}
>
{g}
</button>
))}
</div>
</TaskCard>
))}
</div>
)}
</div>
);
if (isMobile) {
return (
<div style={{ display: "flex", flexDirection: "column", gap: "10px" }}>
{gradeBuckets}
{ungradedPanel}
</div>
);
}
return (
<div style={{ display: "flex", gap: "14px", alignItems: "flex-start" }}>
<div style={{ flex: 3, minWidth: 0 }}>{gradeBuckets}</div>
<div style={{ flex: 2, minWidth: "200px", maxWidth: "380px" }}>{ungradedPanel}</div>
</div>
);
}
// ─────────────────────────────────────────────
@ -1024,9 +1106,10 @@ interface IvyLeeViewProps {
border: string;
textPrimary: string;
textSecondary: string;
isMobile?: boolean;
}
function IvyLeeView({ tasks, selected, darkMode, de, onToggle, onDelegate, onToggleComplete, onUpdateTask, projects, cardBg, border, textPrimary, textSecondary }: IvyLeeViewProps) {
function IvyLeeView({ tasks, selected, darkMode, de, onToggle, onDelegate, onToggleComplete, onUpdateTask, projects, cardBg, border, textPrimary, textSecondary, isMobile }: IvyLeeViewProps) {
const accentColor = "#6366f1";
const selectedTasks = tasks.filter((t) => selected.has(t.id));
const unselected = tasks.filter((t) => !selected.has(t.id));
@ -1041,96 +1124,116 @@ function IvyLeeView({ tasks, selected, darkMode, de, onToggle, onDelegate, onTog
setDropOverTop6(false);
};
return (
<div>
{/* Header info */}
<div style={{ background: darkMode ? "#1e3a5f" : "#eff6ff", borderRadius: "10px", padding: "12px 14px", marginBottom: "16px", display: "flex", alignItems: "flex-start", gap: "10px" }}>
<Star size={16} style={{ color: "#3b82f6", flexShrink: 0, marginTop: "1px" }} />
<div>
<p style={{ fontSize: "0.82rem", color: darkMode ? "#93c5fd" : "#1d4ed8", fontWeight: 600, margin: "0 0 3px" }}>
{de ? "Ivy Lee Methode" : "Ivy Lee Method"}
</p>
<p style={{ fontSize: "0.78rem", color: darkMode ? "#60a5fa" : "#3b82f6", margin: 0, lineHeight: 1.5 }}>
{de
? "Wähle genau 6 wichtige Aufgaben für morgen, geordnet nach Priorität."
: "Select exactly 6 important tasks for tomorrow, ordered by priority."}
</p>
</div>
// Info banner
const infoBanner = (
<div style={{ background: darkMode ? "#1e3a5f" : "#eff6ff", borderRadius: "10px", padding: "12px 14px", marginBottom: "14px", display: "flex", alignItems: "flex-start", gap: "10px" }}>
<Star size={16} style={{ color: "#3b82f6", flexShrink: 0, marginTop: "1px" }} />
<div>
<p style={{ fontSize: "0.82rem", color: darkMode ? "#93c5fd" : "#1d4ed8", fontWeight: 600, margin: "0 0 3px" }}>
{de ? "Ivy Lee Methode" : "Ivy Lee Method"}
</p>
<p style={{ fontSize: "0.78rem", color: darkMode ? "#60a5fa" : "#3b82f6", margin: 0, lineHeight: 1.5 }}>
{de
? "Wähle genau 6 wichtige Aufgaben für morgen, geordnet nach Priorität."
: "Select exactly 6 important tasks for tomorrow, ordered by priority."}
</p>
</div>
</div>
);
{/* Selected tasks */}
<div
onDragOver={(e) => { e.preventDefault(); e.dataTransfer.dropEffect = selected.size < 6 ? "move" : "none"; setDropOverTop6(true); }}
onDragLeave={(e) => { if (!e.currentTarget.contains(e.relatedTarget as Node)) setDropOverTop6(false); }}
onDrop={handleTop6Drop}
style={{ marginBottom: "16px", borderRadius: "10px", border: dropOverTop6 ? `2px dashed ${accentColor}` : "2px solid transparent", padding: "4px", transition: "border 0.15s" }}
>
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: "8px" }}>
<span style={{ fontSize: "0.78rem", fontWeight: 700, color: dropOverTop6 ? accentColor : textSecondary, textTransform: "uppercase", letterSpacing: "0.05em" }}>
{dropOverTop6 ? (de ? "Hier ablegen" : "Drop to add") : (de ? "Meine Top-6" : "My Top 6")}
</span>
<span style={{ fontSize: "0.78rem", color: selected.size === 6 ? "#059669" : accentColor, fontWeight: 600 }}>
{selected.size}/6
</span>
</div>
<div style={{ display: "flex", flexDirection: "column", gap: "6px" }}>
{Array.from({ length: 6 }).map((_, i) => {
const t = selectedTasks[i];
if (t) {
return (
<div key={t.id} style={{ display: "flex", alignItems: "center", gap: "8px" }}>
<span style={{ fontSize: "0.78rem", fontWeight: 700, color: accentColor, width: "16px", textAlign: "center", flexShrink: 0 }}>{i + 1}</span>
<div style={{ flex: 1 }}>
<TaskCard task={t} darkMode={darkMode} cardBg={cardBg} border={border} textPrimary={textPrimary} textSecondary={textSecondary} onDelegate={onDelegate} onToggleComplete={onToggleComplete} onUpdateTask={onUpdateTask} projects={projects} onRemove={() => onToggle(t.id)} de={de} />
</div>
</div>
);
}
// Top-6 selection panel (left column)
const top6Panel = (
<div
onDragOver={(e) => { e.preventDefault(); e.dataTransfer.dropEffect = selected.size < 6 ? "move" : "none"; setDropOverTop6(true); }}
onDragLeave={(e) => { if (!e.currentTarget.contains(e.relatedTarget as Node)) setDropOverTop6(false); }}
onDrop={handleTop6Drop}
style={{ borderRadius: "10px", border: dropOverTop6 ? `2px dashed ${accentColor}` : "2px solid transparent", padding: "4px", transition: "border 0.15s" }}
>
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: "8px" }}>
<span style={{ fontSize: "0.78rem", fontWeight: 700, color: dropOverTop6 ? accentColor : textSecondary, textTransform: "uppercase", letterSpacing: "0.05em" }}>
{dropOverTop6 ? (de ? "Hier ablegen" : "Drop to add") : (de ? "Meine Top-6" : "My Top 6")}
</span>
<span style={{ fontSize: "0.78rem", color: selected.size === 6 ? "#059669" : accentColor, fontWeight: 600 }}>
{selected.size}/6
</span>
</div>
<div style={{ display: "flex", flexDirection: "column", gap: "6px" }}>
{Array.from({ length: 6 }).map((_, i) => {
const t = selectedTasks[i];
if (t) {
return (
<div key={`empty-${i}`} style={{ display: "flex", alignItems: "center", gap: "8px" }}>
<span style={{ fontSize: "0.78rem", fontWeight: 700, color: textSecondary, width: "16px", textAlign: "center", flexShrink: 0 }}>{i + 1}</span>
<div style={{ flex: 1, background: darkMode ? "#1f2937" : "#f9fafb", border: `1px dashed ${border}`, borderRadius: "8px", padding: "10px 12px" }}>
<span style={{ fontSize: "0.78rem", color: textSecondary }}>{de ? "Leer — wähle eine Aufgabe unten" : "Empty — pick a task below"}</span>
<div key={t.id} style={{ display: "flex", alignItems: "center", gap: "8px" }}>
<span style={{ fontSize: "0.78rem", fontWeight: 700, color: accentColor, width: "16px", textAlign: "center", flexShrink: 0 }}>{i + 1}</span>
<div style={{ flex: 1 }}>
<TaskCard task={t} darkMode={darkMode} cardBg={cardBg} border={border} textPrimary={textPrimary} textSecondary={textSecondary} onDelegate={onDelegate} onToggleComplete={onToggleComplete} onUpdateTask={onUpdateTask} projects={projects} onRemove={() => onToggle(t.id)} de={de} />
</div>
</div>
);
})}
</div>
</div>
{/* All other tasks to pick from */}
{unselected.length > 0 && (
<div>
<p style={{ fontSize: "0.78rem", fontWeight: 600, color: textSecondary, marginBottom: "8px" }}>
{de ? "Verfügbare Aufgaben:" : "Available tasks:"}
</p>
<div style={{ display: "flex", flexDirection: "column", gap: "5px" }}>
{unselected.map((t) => (
<div
key={t.id}
onClick={() => onToggle(t.id)}
style={{
background: cardBg,
border: `1px solid ${border}`,
borderRadius: "8px",
padding: "8px 12px",
cursor: selected.size >= 6 ? "not-allowed" : "pointer",
opacity: selected.size >= 6 ? 0.5 : 1,
display: "flex",
alignItems: "center",
gap: "8px",
transition: "background 0.1s",
}}
>
<Circle size={14} style={{ color: "#9ca3af", flexShrink: 0 }} />
<span style={{ fontSize: "0.85rem", color: textPrimary, flex: 1 }}>{t.title}</span>
{t.scheduledDate && <span style={{ fontSize: "0.7rem", color: "#6b7280" }}>{formatDate(t.scheduledDate)}</span>}
{t.project && <span style={{ fontSize: "0.7rem", color: t.project.color || "#6366f1" }}>{t.project.name}</span>}
}
return (
<div key={`empty-${i}`} style={{ display: "flex", alignItems: "center", gap: "8px" }}>
<span style={{ fontSize: "0.78rem", fontWeight: 700, color: textSecondary, width: "16px", textAlign: "center", flexShrink: 0 }}>{i + 1}</span>
<div style={{ flex: 1, background: darkMode ? "#1f2937" : "#f9fafb", border: `1px dashed ${border}`, borderRadius: "8px", padding: "10px 12px" }}>
<span style={{ fontSize: "0.78rem", color: textSecondary }}>{de ? "Leer — wähle eine Aufgabe" : "Empty — pick a task"}</span>
</div>
))}
</div>
);
})}
</div>
</div>
);
// Available tasks pool (right column)
const availablePool = (
<div>
<p style={{ fontSize: "0.78rem", fontWeight: 600, color: textSecondary, marginBottom: "8px" }}>
{unselected.length > 0 ? (de ? "Verfügbare Aufgaben:" : "Available tasks:") : (de ? "Alle Aufgaben ausgewählt ✓" : "All tasks selected ✓")}
</p>
<div style={{ display: "flex", flexDirection: "column", gap: "5px" }}>
{unselected.map((t) => (
<div
key={t.id}
onClick={() => onToggle(t.id)}
style={{
background: cardBg,
border: `1px solid ${border}`,
borderRadius: "8px",
padding: "8px 12px",
cursor: selected.size >= 6 ? "not-allowed" : "pointer",
opacity: selected.size >= 6 ? 0.5 : 1,
display: "flex",
alignItems: "center",
gap: "8px",
transition: "background 0.1s",
}}
>
<Circle size={14} style={{ color: "#9ca3af", flexShrink: 0 }} />
<span style={{ fontSize: "0.85rem", color: textPrimary, flex: 1 }}>{t.title}</span>
{t.scheduledDate && <span style={{ fontSize: "0.7rem", color: "#6b7280" }}>{formatDate(t.scheduledDate)}</span>}
{t.project && <span style={{ fontSize: "0.7rem", color: t.project.color || "#6366f1" }}>{t.project.name}</span>}
</div>
</div>
)}
))}
</div>
</div>
);
if (isMobile) {
return (
<div>
{infoBanner}
{top6Panel}
{unselected.length > 0 && <div style={{ marginTop: "16px" }}>{availablePool}</div>}
</div>
);
}
return (
<div>
{infoBanner}
<div style={{ display: "flex", gap: "14px", alignItems: "flex-start" }}>
<div style={{ flex: 1, minWidth: 0 }}>{top6Panel}</div>
<div style={{ flex: 1, minWidth: "200px" }}>{availablePool}</div>
</div>
</div>
);
}
@ -1151,17 +1254,17 @@ interface ParetoViewProps {
border: string;
textPrimary: string;
textSecondary: string;
isMobile?: boolean;
}
function ParetoView({ important, rest, darkMode, de, onDelegate, onToggleComplete, onUpdateTask, projects, cardBg, border, textPrimary, textSecondary }: ParetoViewProps) {
const [showRest, setShowRest] = useState(false);
function ParetoView({ important, rest, darkMode, de, onDelegate, onToggleComplete, onUpdateTask, projects, cardBg, border, textPrimary, textSecondary, isMobile }: ParetoViewProps) {
const total = important.length + rest.length;
const pct = total > 0 ? Math.round((important.length / total) * 100) : 0;
return (
<div>
{/* Info banner */}
<div style={{ background: darkMode ? "#422006" : "#fff7ed", borderRadius: "10px", padding: "12px 14px", marginBottom: "16px", display: "flex", gap: "10px", alignItems: "flex-start" }}>
// Info banner + progress bar (full-width header)
const header = (
<>
<div style={{ background: darkMode ? "#422006" : "#fff7ed", borderRadius: "10px", padding: "12px 14px", marginBottom: "12px", display: "flex", gap: "10px", alignItems: "flex-start" }}>
<BarChart2 size={16} style={{ color: "#f97316", flexShrink: 0, marginTop: "1px" }} />
<div>
<p style={{ fontSize: "0.82rem", fontWeight: 700, color: "#f97316", margin: "0 0 3px" }}>
@ -1169,66 +1272,87 @@ function ParetoView({ important, rest, darkMode, de, onDelegate, onToggleComplet
</p>
<p style={{ fontSize: "0.78rem", color: darkMode ? "#fb923c" : "#c2410c", margin: 0, lineHeight: 1.5 }}>
{de
? `${important.length} von ${total} Aufgaben (${pct}%) sind als wichtig markiert. Fokussiere dich auf diese, um 80% der Ergebnisse zu erzielen.`
: `${important.length} of ${total} tasks (${pct}%) are marked as important. Focus on these to achieve 80% of your outcomes.`}
? `${important.length} von ${total} Aufgaben (${pct}%) sind wichtig. Fokussiere dich auf diese für 80% der Ergebnisse.`
: `${important.length} of ${total} tasks (${pct}%) are marked as important. Focus on these to achieve 80% of outcomes.`}
</p>
</div>
</div>
{/* Progress bar */}
<div style={{ background: darkMode ? "#374151" : "#e5e7eb", borderRadius: "99px", height: "8px", marginBottom: "20px", overflow: "hidden" }}>
<div style={{ background: darkMode ? "#374151" : "#e5e7eb", borderRadius: "99px", height: "8px", marginBottom: "16px", overflow: "hidden" }}>
<div style={{ width: `${pct}%`, height: "100%", background: "linear-gradient(90deg, #f97316, #ef4444)", borderRadius: "99px", transition: "width 0.4s" }} />
</div>
</>
);
{/* Important tasks */}
<div style={{ marginBottom: "16px" }}>
<div style={{ display: "flex", alignItems: "center", gap: "8px", marginBottom: "10px" }}>
<span style={{ fontSize: "0.78rem", fontWeight: 700, color: "#f97316", textTransform: "uppercase", letterSpacing: "0.05em" }}>
{de ? "Wichtige Aufgaben (Fokus)" : "High-Impact Tasks (Focus)"}
</span>
<span style={{ fontSize: "0.75rem", color: "#f97316", background: "#f9731622", borderRadius: "9px", padding: "1px 7px" }}>{important.length}</span>
</div>
// High-impact tasks (left column)
const importantPanel = (
<div>
<div style={{ display: "flex", alignItems: "center", gap: "8px", marginBottom: "10px" }}>
<span style={{ fontSize: "0.78rem", fontWeight: 700, color: "#f97316", textTransform: "uppercase", letterSpacing: "0.05em" }}>
{de ? "Wichtige Aufgaben (Fokus)" : "High-Impact Tasks (Focus)"}
</span>
<span style={{ fontSize: "0.75rem", color: "#f97316", background: "#f9731622", borderRadius: "9px", padding: "1px 7px" }}>{important.length}</span>
</div>
<div style={{ display: "flex", flexDirection: "column", gap: "6px" }}>
{important.length === 0 && (
<p style={{ fontSize: "0.8rem", color: textSecondary, padding: "10px 0" }}>
{de
? "Keine wichtigen Aufgaben. Markiere Aufgaben als wichtig im Eisenhower- oder ABCDE-Modus."
: "No important tasks. Mark tasks as important via Eisenhower or ABCDE mode."}
</p>
)}
{important.map((t) => (
<div key={t.id} style={{ borderLeft: "3px solid #f97316", borderRadius: "0 8px 8px 0" }}>
<TaskCard task={t} darkMode={darkMode} cardBg={cardBg} border={border} textPrimary={textPrimary} textSecondary={textSecondary} onDelegate={onDelegate} onToggleComplete={onToggleComplete} onUpdateTask={onUpdateTask} projects={projects} onRemove={() => onUpdateTask(t.id, { importance: false })} de={de} />
</div>
))}
</div>
</div>
);
// Remaining tasks (right column — always visible, no toggle)
const remainingPanel = (
<div>
<div style={{ display: "flex", alignItems: "center", gap: "8px", marginBottom: "10px" }}>
<span style={{ fontSize: "0.78rem", fontWeight: 600, color: textSecondary, textTransform: "uppercase", letterSpacing: "0.05em" }}>
{de ? "Restliche Aufgaben" : "Remaining tasks"}
</span>
<span style={{ fontSize: "0.75rem", color: textSecondary, background: darkMode ? "#374151" : "#e5e7eb", borderRadius: "9px", padding: "1px 7px" }}>{rest.length}</span>
</div>
{rest.length === 0 ? (
<p style={{ fontSize: "0.78rem", color: textSecondary }}>{de ? "Keine weiteren Aufgaben." : "No remaining tasks."}</p>
) : (
<div style={{ display: "flex", flexDirection: "column", gap: "6px" }}>
{important.length === 0 && (
<p style={{ fontSize: "0.8rem", color: textSecondary, padding: "10px 0" }}>
{de
? "Keine wichtigen Aufgaben. Markiere Aufgaben als wichtig im Eisenhower- oder ABCDE-Modus."
: "No important tasks. Mark tasks as important via Eisenhower or ABCDE mode."}
</p>
)}
{important.map((t) => (
<div key={t.id} style={{ borderLeft: "3px solid #f97316", borderRadius: "0 8px 8px 0" }}>
<TaskCard task={t} darkMode={darkMode} cardBg={cardBg} border={border} textPrimary={textPrimary} textSecondary={textSecondary} onDelegate={onDelegate} onToggleComplete={onToggleComplete} onUpdateTask={onUpdateTask} projects={projects} onRemove={() => onUpdateTask(t.id, { importance: false })} de={de} />
</div>
{rest.map((t) => (
<TaskCard key={t.id} task={t} darkMode={darkMode} cardBg={cardBg} border={border} textPrimary={textPrimary} textSecondary={textSecondary} onDelegate={onDelegate} onToggleComplete={onToggleComplete} onUpdateTask={onUpdateTask} projects={projects} de={de}>
<button
onClick={() => onUpdateTask(t.id, { importance: true })}
style={{ fontSize: "0.65rem", marginTop: "3px", background: "none", border: "none", color: "#f97316", cursor: "pointer", padding: 0, display: "flex", alignItems: "center", gap: "2px" }}
>
<Star size={9} /> {de ? "Als wichtig markieren" : "Mark as important"}
</button>
</TaskCard>
))}
</div>
</div>
)}
</div>
);
{/* The rest (80%) */}
if (isMobile) {
return (
<div>
<button
onClick={() => setShowRest((v) => !v)}
style={{ display: "flex", alignItems: "center", gap: "6px", background: "none", border: "none", cursor: "pointer", padding: "6px 0", marginBottom: "8px" }}
>
<span style={{ fontSize: "0.78rem", fontWeight: 600, color: textSecondary, textTransform: "uppercase", letterSpacing: "0.05em" }}>
{de ? "Restliche Aufgaben" : "Remaining tasks"} ({rest.length})
</span>
{showRest ? <ChevronUp size={13} style={{ color: textSecondary }} /> : <ChevronDown size={13} style={{ color: textSecondary }} />}
</button>
{showRest && (
<div style={{ display: "flex", flexDirection: "column", gap: "6px" }}>
{rest.map((t) => (
<TaskCard key={t.id} task={t} darkMode={darkMode} cardBg={cardBg} border={border} textPrimary={textPrimary} textSecondary={textSecondary} onDelegate={onDelegate} onToggleComplete={onToggleComplete} onUpdateTask={onUpdateTask} projects={projects} de={de}>
<button
onClick={() => onUpdateTask(t.id, { importance: true })}
style={{ fontSize: "0.65rem", marginTop: "3px", background: "none", border: "none", color: "#f97316", cursor: "pointer", padding: 0, display: "flex", alignItems: "center", gap: "2px" }}
>
<Star size={9} /> {de ? "Als wichtig markieren" : "Mark as important"}
</button>
</TaskCard>
))}
</div>
)}
{header}
{importantPanel}
<div style={{ marginTop: "16px" }}>{remainingPanel}</div>
</div>
);
}
return (
<div>
{header}
<div style={{ display: "flex", gap: "14px", alignItems: "flex-start" }}>
<div style={{ flex: 1, minWidth: 0 }}>{importantPanel}</div>
<div style={{ flex: 1, minWidth: "200px" }}>{remainingPanel}</div>
</div>
</div>
);