Add LICENSE, README, and Docs tab to Mission Control

This commit is contained in:
root
2026-02-22 07:33:18 +00:00
parent 3e7b457d5f
commit 0817444dc5
68 changed files with 6677 additions and 1673 deletions
@@ -0,0 +1,279 @@
'use client'
import { useState, useEffect } from 'react'
import { Task, TaskStatus } from '@/lib/mission-control/types'
interface TaskCardsPanelProps {
tasks: Task[]
toggleTask: (id: string) => void
}
const projects = ['sitemente', 'holacompi', 'arabredox', 'infrastructure', 'trading']
export function TaskCardsPanel({ tasks, toggleTask }: TaskCardsPanelProps) {
const [selectedProject, setSelectedProject] = useState<string>('sitemente')
const [selectedTaskId, setSelectedTaskId] = useState<string | null>(null)
const [command, setCommand] = useState('')
const [responses, setResponses] = useState<{id: string, task: string, command: string, reply: string, createdAt: string}[]>([])
// Poll for replies every 10 seconds
useEffect(() => {
const pollReplies = async () => {
try {
const res = await fetch('/api/command-history')
if (res.ok) {
const data = await res.json()
// Get entries with replies that haven't been shown
const withReplies = (data.history || []).filter((h: any) => h.reply && h.reply.length > 0)
setResponses(withReplies.slice(-5).reverse())
}
} catch (e) {}
}
pollReplies()
const interval = setInterval(pollReplies, 10000)
return () => clearInterval(interval)
}, [])
const projectTasks = tasks.filter(t =>
t.project === selectedProject &&
(t.status === 'todo' || t.status === 'in_progress')
)
const handleTaskClick = (taskId: string) => {
setSelectedTaskId(selectedTaskId === taskId ? null : taskId)
setCommand('')
}
const handleConfirm = () => {
if (!selectedTaskId || !command.trim()) return
const task = tasks.find(t => t.id === selectedTaskId)
if (!task) return
fetch('/api/command-history', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
task: task.title,
command: command,
project: selectedProject,
action: 'continue-task'
})
}).catch(() => {})
fetch('/api/command', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
command: command,
task: task.title,
action: 'continue-task'
})
}).catch(() => {})
alert(`✅ Sent to Horus: "${command}"`)
setCommand('')
setSelectedTaskId(null)
}
const handleQuickChat = () => {
if (!command.trim()) return
// Save quick chat to history - this will notify Horus via the API
fetch('/api/command-history', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
task: `Quick message - ${selectedProject}`,
command: command,
project: selectedProject,
action: 'quick-message'
})
}).catch(() => {})
alert(`✅ Sent to Horus! I'll reply shortly.`)
setCommand('')
}
const selectedTask = tasks.find(t => t.id === selectedTaskId)
return (
<div className="space-y-4">
{/* Project Tabs */}
<div className="flex gap-2 flex-wrap">
{projects.map(project => {
const count = tasks.filter(t =>
t.project === project &&
(t.status === 'todo' || t.status === 'in_progress')
).length
return (
<button
key={project}
onClick={() => {
setSelectedProject(project)
setSelectedTaskId(null)
}}
className={`px-4 py-2 rounded-lg text-sm font-medium transition ${
selectedProject === project
? 'bg-brand-pink text-white'
: 'bg-white/10 text-white/70 hover:bg-white/20'
}`}
>
{project.charAt(0).toUpperCase() + project.slice(1)}
<span className="ml-2 text-xs opacity-70">({count})</span>
</button>
)
})}
</div>
{/* Task Cards */}
<div className="border border-white/20 rounded-lg p-4 bg-white/5">
<h3 className="text-lg font-bold mb-4">
📋 {selectedProject.toUpperCase()} TASKS ({projectTasks.length})
</h3>
<div className="space-y-2">
{projectTasks.map((task) => (
<div
key={task.id}
className={`p-3 rounded-lg border transition-all ${
selectedTaskId === task.id
? 'border-brand-pink bg-brand-pink/10'
: 'border-white/10 bg-white/5 hover:border-white/30'
}`}
onClick={() => handleTaskClick(task.id)}
>
<div className="flex items-center gap-3">
<button
onClick={(e) => {
e.stopPropagation()
toggleTask(task.id)
}}
className={`w-5 h-5 rounded-full border-2 flex items-center justify-center transition ${
task.status === 'done'
? 'border-green-500 bg-green-500'
: task.status === 'in_progress'
? 'border-yellow-500 bg-yellow-500'
: 'border-white/30 hover:border-white/50'
}`}
>
{task.status === 'done' && (
<svg className="w-3 h-3 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={3} d="M5 13l4 4L19 7" />
</svg>
)}
</button>
<div className="flex-1">
<p className={`font-medium ${task.status === 'done' ? 'line-through opacity-50' : ''}`}>
{task.title}
</p>
{task.description && (
<p className="text-xs text-white/50 truncate">{task.description}</p>
)}
</div>
<span className="text-lg">
{selectedTaskId === task.id ? '▼' : '▶'}
</span>
</div>
{/* Expanded Input */}
{selectedTaskId === task.id && (
<div className="mt-3 ml-8">
<textarea
value={command}
onChange={(e) => setCommand(e.target.value)}
placeholder={`What to do with "${task.title}"?`}
className="w-full px-3 py-2 bg-black/50 border border-white/20 rounded text-sm text-white placeholder:text-white/40 focus:outline-none focus:border-brand-pink resize-none"
rows={3}
onClick={(e) => e.stopPropagation()}
autoFocus
/>
<div className="flex gap-2 mt-2">
<button
onClick={(e) => {
e.stopPropagation()
handleConfirm()
}}
disabled={!command.trim()}
className={`px-4 py-1.5 rounded text-xs font-bold ${
command.trim()
? 'bg-brand-pink text-white hover:bg-[#ff7bc0]'
: 'bg-white/10 text-white/30 cursor-not-allowed'
}`}
>
SEND TO HORUS
</button>
<button
onClick={(e) => {
e.stopPropagation()
setSelectedTaskId(null)
setCommand('')
}}
className="px-3 py-1.5 text-xs text-white/60 hover:text-white"
>
Cancel
</button>
</div>
</div>
)}
</div>
))}
</div>
{projectTasks.length === 0 && (
<p className="text-white/50 text-center py-4">No pending tasks </p>
)}
{/* Quick Chat - always available */}
<div className="mt-4 pt-4 border-t border-white/10">
<p className="text-xs text-white/50 mb-2">💬 Quick message for {selectedProject}</p>
<div className="flex gap-2">
<textarea
value={command}
onChange={(e) => setCommand(e.target.value)}
placeholder={`New task or message for ${selectedProject}...`}
className="flex-1 px-3 py-2 bg-black/50 border border-white/20 rounded text-sm text-white placeholder:text-white/40 focus:outline-none focus:border-brand-pink resize-none"
rows={2}
onKeyDown={(e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault()
handleQuickChat()
}
}}
/>
<button
onClick={handleQuickChat}
disabled={!command.trim()}
className={`px-4 rounded text-sm font-bold ${
command.trim()
? 'bg-brand-pink text-white hover:bg-[#ff7bc0]'
: 'bg-white/10 text-white/30 cursor-not-allowed'
}`}
>
</button>
</div>
</div>
{/* Responses from Horus */}
{responses.length > 0 && (
<div className="mt-4 pt-4 border-t border-white/10">
<p className="text-xs text-white/50 mb-2">💬 Recent replies from Horus</p>
<div className="space-y-2 max-h-40 overflow-y-auto">
{responses.map((r) => (
<div key={r.id} className="p-2 rounded bg-brand-pink/10 border border-brand-pink/30 text-sm">
<p className="text-white/60 text-xs">You: {r.command}</p>
<p className="text-brand-pink mt-1">👁 {r.reply}</p>
</div>
))}
</div>
</div>
)}
</div>
</div>
)
}