From eaaa556448fa8cc991de61f0584ec9c477d1cbeb Mon Sep 17 00:00:00 2001 From: root Date: Mon, 16 Feb 2026 13:09:49 +0000 Subject: [PATCH] feat: Add search, quick add task, export, today's focus, keyboard shortcuts --- .../MissionControlDashboard.tsx | 445 ++++++++---------- lib/mission-control/store.tsx | 11 + 2 files changed, 202 insertions(+), 254 deletions(-) diff --git a/components/mission-control/MissionControlDashboard.tsx b/components/mission-control/MissionControlDashboard.tsx index 3901447..ef7e0b3 100644 --- a/components/mission-control/MissionControlDashboard.tsx +++ b/components/mission-control/MissionControlDashboard.tsx @@ -1,6 +1,6 @@ "use client"; -import { useState } from "react"; +import { useState, useEffect } from "react"; import { useMissionControl } from "@/lib/mission-control/store"; import { TaskStatus } from "@/lib/mission-control/types"; import VoiceChat from "./VoiceChat"; @@ -22,55 +22,25 @@ interface SidebarCategory { } const sidebarCategories: SidebarCategory[] = [ - { - id: "projects", - name: "Projects", - icon: "🎯", - items: [ - { id: "sitemente", name: "SiteMente", icon: "🌐", color: "#ff7bc0", category: "projects" }, - { id: "holacompi", name: "HolaCompi", icon: "🤝", color: "#6366f1", category: "projects" }, - ], - }, - { - id: "tasks", - name: "Tasks", - icon: "✓", - items: [ - { id: "all", name: "All Tasks", icon: "📋", category: "tasks" }, - ], - }, - { - id: "chat", - name: "Chat", - icon: "💬", - items: [ - { id: "voice", name: "Voice Chat", icon: "🎤", category: "chat" }, - ], - }, - { - id: "council", - name: "Council", - icon: "🏛️", - items: [ - { id: "ai-settings", name: "AI Settings", icon: "🤖", category: "council" }, - ], - }, - { - id: "calendar", - name: "Calendar", - icon: "📅", - items: [ - { id: "brief", name: "Morning Brief", icon: "☀️", category: "calendar" }, - ], - }, - { - id: "memory", - name: "Memory", - icon: "🧠", - items: [ - { id: "logs", name: "Session Logs", icon: "📝", category: "memory" }, - ], - }, + { id: "projects", name: "Projects", icon: "🎯", items: [ + { id: "sitemente", name: "SiteMente", icon: "🌐", color: "#ff7bc0", category: "projects" }, + { id: "holacompi", name: "HolaCompi", icon: "🤝", color: "#6366f1", category: "projects" }, + ]}, + { id: "tasks", name: "Tasks", icon: "✓", items: [ + { id: "all", name: "All Tasks", icon: "📋", category: "tasks" }, + ]}, + { id: "chat", name: "Chat", icon: "💬", items: [ + { id: "voice", name: "Voice Chat", icon: "🎤", category: "chat" }, + ]}, + { id: "council", name: "Council", icon: "🏛️", items: [ + { id: "ai-settings", name: "AI Settings", icon: "🤖", category: "council" }, + ]}, + { id: "calendar", name: "Calendar", icon: "📅", items: [ + { id: "brief", name: "Morning Brief", icon: "☀️", category: "calendar" }, + ]}, + { id: "memory", name: "Memory", icon: "🧠", items: [ + { id: "logs", name: "Session Logs", icon: "📝", category: "memory" }, + ]}, ]; const statusConfig: Record = { @@ -82,11 +52,37 @@ const statusConfig: Record = { }; export default function MissionControlDashboard() { - const { tasks, toggleTask, updateTaskStatus, getProjectProgress, getTasksByProject } = useMissionControl(); - + const { tasks, toggleTask, updateTaskStatus, addTask, getProjectProgress, getTasksByProject } = useMissionControl(); const [selectedItem, setSelectedItem] = useState("sitemente"); const [filter, setFilter] = useState("all"); const [expandedCategories, setExpandedCategories] = useState(["projects"]); + const [searchQuery, setSearchQuery] = useState(""); + const [showAddTask, setShowAddTask] = useState(false); + const [newTaskTitle, setNewTaskTitle] = useState(""); + const [newTaskProject, setNewTaskProject] = useState("sitemente"); + + // Keyboard shortcuts + useEffect(() => { + const handleKeyDown = (e: KeyboardEvent) => { + if (e.key === "/" && !e.ctrlKey && !e.metaKey) { + const target = e.target as HTMLElement; + if (target.tagName !== "INPUT" && target.tagName !== "TEXTAREA") { + e.preventDefault(); + document.getElementById("search-input")?.focus(); + } + } + if (e.key === "n" && (e.ctrlKey || e.metaKey)) { + e.preventDefault(); + setShowAddTask(true); + } + if (e.key === "Escape") { + setShowAddTask(false); + setSearchQuery(""); + } + }; + window.addEventListener("keydown", handleKeyDown); + return () => window.removeEventListener("keydown", handleKeyDown); + }, []); // Find selected item let selectedCategory = sidebarCategories.find(c => c.items.some(i => i.id === selectedItem)); @@ -94,255 +90,196 @@ export default function MissionControlDashboard() { const projectTasks = currentItem?.category === "projects" ? getTasksByProject(selectedItem as any) : tasks; const filteredTasks = filter === "all" ? projectTasks : projectTasks.filter((t) => t.status === filter); + const searchedTasks = searchQuery + ? filteredTasks.filter(t => t.title.toLowerCase().includes(searchQuery.toLowerCase())) + : filteredTasks; const progress = currentItem?.category === "projects" ? getProjectProgress(selectedItem as any) : 0; const toggleCategory = (catId: string) => { - setExpandedCategories(prev => - prev.includes(catId) ? prev.filter(id => id !== catId) : [...prev, catId] - ); + setExpandedCategories(prev => prev.includes(catId) ? prev.filter(id => id !== catId) : [...prev, catId]); }; const collapseAll = () => setExpandedCategories([]); - const expandAll = () => setExpandedCategories(sidebarCategories.map(c => c.id)); - // Render content based on selection + // Today's focus + const todayTasks = projectTasks.filter(t => t.priority === "critical" || t.status === "in_progress").slice(0, 3); + + // Export function + const exportTasks = () => { + const data = JSON.stringify(projectTasks, null, 2); + const blob = new Blob([data], { type: "application/json" }); + const url = URL.createObjectURL(blob); + const a = document.createElement("a"); + a.href = url; + a.download = `tasks-${new Date().toISOString().split("T")[0]}.json`; + a.click(); + }; + + // Add task + const handleAddTask = () => { + if (!newTaskTitle.trim()) return; + addTask({ title: newTaskTitle, description: "", status: "todo", priority: "medium", project: newTaskProject as any }); + setShowAddTask(false); + setNewTaskTitle(""); + }; + + // Render functions + const renderSidebar = () => ( + + ); + + const renderHeader = () => ( +
+
+ +
+

{currentItem?.icon}{currentItem?.name}

+

{currentItem?.category === "projects" ? `${progress}% complete` : `${tasks.length} total tasks`}

+
+
+ {currentItem?.category === "projects" && ( +
+ + + + +
+ )} +
+ ); + const renderContent = () => { const category = currentItem?.category; - - if (category === "chat") { - return ( + if (category === "chat") return
; + if (category === "council") return
; + if (category === "calendar") return ( +
- +

📅 Morning Brief

+

Daily intelligence at 6am CET

+ ☀️ Open Calendar
- ); - } - - if (category === "council") { - return ( -
- -
- ); - } - - if (category === "calendar") { - return ( -
-
-

📅 Morning Brief

-

Daily intelligence at 6am CET

- - ☀️ Open Calendar - -
- -
- ); - } - - if (category === "memory") { - return ( -
-

🧠 Memory & Logs

-

Session history and daily logs

-
-

Memory is stored in:

-
    -
  • • localStorage (browser)
  • -
  • • GitHub repo (daily commits)
  • -
  • • MEMORY.md (curated)
  • -
-
-
- ); - } - - // Default: Tasks view + +
+ ); + if (category === "memory") return ( +
+

🧠 Memory & Logs

+

Session history and daily logs

+

Memory is stored in:

  • • localStorage (browser)
  • • GitHub repo (daily commits)
  • • MEMORY.md (curated)
+
+ ); return renderTasksView(); }; const renderTasksView = () => ( <> - {/* Stats Row */} -
+ {todayTasks.length > 0 && ( +
+

🎯 TODAY'S FOCUS

+
{todayTasks.map(task => ( +
+ + {task.title} +
+ ))}
+
+ )} +
+
+ 🔍 + setSearchQuery(e.target.value)} placeholder="Search tasks... (press /)" className="w-full bg-white/10 border border-white/20 rounded-lg pl-9 pr-4 py-2 text-sm placeholder:text-white/40 focus:outline-none focus:border-brand-pink" /> +
+ + +
+ {showAddTask && ( +
+ setNewTaskTitle(e.target.value)} placeholder="New task title..." className="w-full bg-white/10 border border-white/20 rounded-lg px-4 py-2 text-sm placeholder:text-white/40 focus:outline-none focus:border-brand-pink mb-3" autoFocus onKeyDown={(e) => e.key === "Enter" && handleAddTask()} /> +
+ + + +
+
+ )} +
{(["todo", "in_progress", "done", "blocked"] as TaskStatus[]).map((status) => { const count = projectTasks.filter((t) => t.status === status).length; const config = statusConfig[status]; return ( - ); })}
- - {/* Task List */}
-

{currentItem?.name || "Tasks"}

-

- {filteredTasks.filter((t) => t.status === "done").length} / {filteredTasks.length} done -

+

{currentItem?.name || "Tasks"}{searchQuery && ({searchedTasks.length} results)}

+

{filteredTasks.filter((t) => t.status === "done").length} / {filteredTasks.length} done

-
- {filteredTasks.map((task) => { + {searchedTasks.map((task) => { const config = statusConfig[task.status]; return ( -
- - -
-

- {task.title} -

-
- - {task.priority === "critical" && ( - CRITICAL - )} - - updateTaskStatus(task.id, e.target.value as TaskStatus)} className="text-xs bg-transparent border border-white/20 rounded px-2 py-1"> +
); })}
- - {filteredTasks.length === 0 && ( -
- No tasks match the current filter. -
- )} + {searchedTasks.length === 0 &&
No tasks match the current filter.
}
); return (
- {/* Left Sidebar */} - - - {/* Main Content */} + {renderSidebar()}
- {/* Header */} -
-
- -
-

- {currentItem?.icon} - {currentItem?.name} -

-

- {currentItem?.category === "projects" ? `${progress}% complete` : `${tasks.length} total tasks`} -

-
-
- {currentItem?.category === "projects" && ( -
- - - - -
- )} -
- - {/* Content */} + {renderHeader()} {renderContent()}
diff --git a/lib/mission-control/store.tsx b/lib/mission-control/store.tsx index c7d9e22..622ba14 100644 --- a/lib/mission-control/store.tsx +++ b/lib/mission-control/store.tsx @@ -7,6 +7,7 @@ interface MissionControlStore { tasks: Task[]; toggleTask: (id: string) => void; updateTaskStatus: (id: string, status: TaskStatus) => void; + addTask: (task: Omit) => void; getProjectProgress: (projectId: string) => number; getTasksByProject: (projectId: string) => Task[]; } @@ -68,6 +69,15 @@ export function MissionControlProvider({ children }: { children: ReactNode }) { ); }; + const addTask = (task: Omit) => { + const newTask: Task = { + ...task, + id: `t-${Date.now()}`, + order: tasks.length + 1, + }; + setTasks((prev) => [...prev, newTask]); + }; + const getProjectProgress = (projectId: string) => { const projectTasks = tasks.filter((t) => t.project === projectId); if (projectTasks.length === 0) return 0; @@ -85,6 +95,7 @@ export function MissionControlProvider({ children }: { children: ReactNode }) { tasks, toggleTask, updateTaskStatus, + addTask, getProjectProgress, getTasksByProject, }}