diff --git a/app/api/data/route.ts b/app/api/data/route.ts new file mode 100644 index 0000000..216f938 --- /dev/null +++ b/app/api/data/route.ts @@ -0,0 +1,79 @@ +import { NextResponse } from 'next/server'; +import * as fs from 'fs'; +import * as path from 'path'; + +const DATA_DIR = '/app/data'; + +// Tasks API +export async function GET(request: Request) { + const { searchParams } = new URL(request.url); + const type = searchParams.get('type') || 'tasks'; + + try { + if (type === 'tasks') { + const tasksDir = path.join(DATA_DIR, 'shared-context/jelena-neo-tasks/archive'); + const tasks: any[] = []; + + if (fs.existsSync(tasksDir)) { + const files = fs.readdirSync(tasksDir).filter(f => f.endsWith('.md')); + for (const file of files) { + const content = fs.readFileSync(path.join(tasksDir, file), 'utf-8'); + const title = content.match(/^# Task: (.+)$/m)?.[1] || file; + const created = content.match(/Created: (.+)$/m)?.[1] || ''; + const priority = content.match(/Priority: (.+)$/m)?.[1] || 'normal'; + + tasks.push({ + id: file.replace('.md', ''), + title, + created, + priority, + status: 'done', + assignee: 'neo' + }); + } + } + return NextResponse.json(tasks); + } + + if (type === 'crons') { + const cronFile = path.join(DATA_DIR, 'cron/jobs.json'); + if (fs.existsSync(cronFile)) { + const data = JSON.parse(fs.readFileSync(cronFile, 'utf-8')); + const jobs = (data.jobs || []).map((job: any) => ({ + name: job.name, + enabled: job.enabled, + status: job.state?.lastStatus || 'unknown', + lastRun: job.state?.lastRunAtMs ? new Date(job.state.lastRunAtMs).toISOString() : null + })); + return NextResponse.json(jobs); + } + return NextResponse.json([]); + } + + if (type === 'memory') { + const memoryDir = path.join(DATA_DIR, 'memory'); + const memories: any[] = []; + + if (fs.existsSync(memoryDir)) { + const files = fs.readdirSync(memoryDir).filter(f => f.endsWith('.md')); + for (const file of files.slice(0, 20)) { + const content = fs.readFileSync(path.join(memoryDir, file), 'utf-8'); + const title = content.match(/^# (.+)$/m)?.[1] || file; + const preview = content.substring(0, 150).replace(/[#*]/g, ''); + + memories.push({ + id: file.replace('.md', ''), + title, + date: file.substring(0, 10), + preview + }); + } + } + return NextResponse.json(memories); + } + + return NextResponse.json({ error: 'Unknown type' }, { status: 400 }); + } catch (e: any) { + return NextResponse.json({ error: e.message }, { status: 500 }); + } +} diff --git a/app/page.tsx b/app/page.tsx index a656c35..e5abcb0 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,16 +1,13 @@ -// Mission Control - Next.js App -// Track tasks, content, calendar, memory, team, and office - -import { useState } from 'react'; +import { useState, useEffect } from 'react'; // Types type TaskStatus = 'todo' | 'in-progress' | 'done'; -type Task = { id: string; title: string; status: TaskStatus; assignee: 'me' | 'jelena' | 'neo' }; +type Task = { id: string; title: string; status: TaskStatus; assignee: 'me' | 'jelena' | 'neo'; priority?: string; created?: string }; type ContentStage = 'idea' | 'script' | 'thumbnail' | 'filming' | 'done'; -type ContentItem = { id: string; title: string; stage: ContentStage; script?: string }; +type ContentItem = { id: string; title: string; stage: ContentStage }; -type CalendarEvent = { id: string; title: string; date: string; type: 'cron' | 'scheduled' }; +type CronJob = { name: string; enabled: boolean; status: string; lastRun?: string }; type Memory = { id: string; title: string; date: string; preview: string }; @@ -18,6 +15,30 @@ type TeamMember = { id: string; name: string; role: string; status: 'working' | export default function MissionControl() { const [activeTab, setActiveTab] = useState('tasks'); + const [tasks, setTasks] = useState([]); + const [crons, setCrons] = useState([]); + const [loading, setLoading] = useState(true); + + useEffect(() => { + async function fetchData() { + try { + // Fetch tasks from mounted volume + const tasksRes = await fetch('/api/data?type=tasks'); + const tasksData = await tasksRes.json(); + setTasks(tasksData); + + // Fetch cron jobs + const cronsRes = await fetch('/api/data?type=crons'); + const cronsData = await cronsRes.json(); + setCrons(cronsData); + } catch (e) { + console.error('Failed to fetch data:', e); + } finally { + setLoading(false); + } + } + fetchData(); + }, []); return (
@@ -25,7 +46,7 @@ export default function MissionControl() {

🎯 Mission Control

); } // ============ TASKS BOARD ============ -function TasksBoard() { - const [tasks, setTasks] = useState([ - { id: '1', title: 'Fix git backup push', status: 'in-progress', assignee: 'neo' }, - { id: '2', title: 'Deploy nodecrew landing page', status: 'done', assignee: 'neo' }, - { id: '3', title: 'Fix YouTube backup', status: 'todo', assignee: 'neo' }, - { id: '4', title: 'Setup Mission Control', status: 'in-progress', assignee: 'jelena' }, - ]); - +function TasksBoard({ tasks }: { tasks: Task[] }) { const columns: { status: TaskStatus; label: string; color: string }[] = [ { status: 'todo', label: 'To Do', color: '#6b7280' }, { status: 'in-progress', label: 'In Progress', color: '#e94560' }, @@ -84,129 +102,63 @@ function TasksBoard() { @{task.assignee} ))} + {tasks.filter(t => t.status === col.status).length === 0 && ( +

No tasks

+ )} ))} ); } -// ============ CONTENT PIPELINE ============ -function ContentPipeline() { - const [items, setItems] = useState([ - { id: '1', title: 'ManoonOils Ad Copy', stage: 'script' }, - { id: '2', title: 'AI Agency Promo', stage: 'idea' }, - { id: '3', title: 'Product Launch Video', stage: 'thumbnail' }, - ]); - - const stages: { stage: ContentStage; label: string }[] = [ - { stage: 'idea', label: '💡 Ideas' }, - { stage: 'script', label: '📝 Script' }, - { stage: 'thumbnail', label: '🖼️ Thumbnail' }, - { stage: 'filming', label: '🎬 Filming' }, - { stage: 'done', label: '✅ Done' }, - ]; - - return ( -
- {stages.map(s => ( -
-

{s.label}

- {items.filter(i => i.stage === s.stage).map(item => ( -
- {item.title} -
- ))} -
- ))} -
- ); -} - -// ============ CALENDAR ============ -function Calendar() { - const events: CalendarEvent[] = [ - { id: '1', title: 'Twitter Briefing', date: '2026-02-19 06:00', type: 'cron' }, - { id: '2', title: 'Infra Status Report', date: '2026-02-19 07:00', type: 'cron' }, - { id: '3', title: 'ManoonOils Analytics', date: '2026-02-19 08:00', type: 'cron' }, - { id: '4', title: 'YouTube Backup', date: '2026-02-18 21:00', type: 'cron' }, - ]; - +// ============ CRON MONITOR ============ +function CronMonitor({ crons }: { crons: CronJob[] }) { return (
-

📅 Scheduled Tasks & Cron Jobs

+

⏰ Cron Jobs

- - - + + + - {events.map(event => ( - - - + {crons.map((cron, i) => ( + + + ))} + {crons.length === 0 && ( + + + + )}
TaskDate/TimeTypeJobStatusEnabled
{event.title}{event.date}
{cron.name} - {event.type} + {cron.status} + {cron.enabled ? '✅' : '❌'} +
+ No cron data available +
); } -// ============ MEMORY ============ -function Memory() { - const [search, setSearch] = useState(''); - - const memories: Memory[] = [ - { id: '1', title: 'ManoonOils Product Formula', date: '2026-02-07', preview: 'Professional formulation for anti-aging serum...' }, - { id: '2', title: 'Backup System Setup', date: '2026-02-05', preview: 'Storage Box mounted at /mnt/storagebox...' }, - { id: '3', title: 'Neo Agent Created', date: '2026-02-03', preview: 'Neo as CTO - infrastructure agent...' }, - ]; - - const filtered = memories.filter(m => m.title.toLowerCase().includes(search.toLowerCase())); - - return ( -
- setSearch(e.target.value)} - style={{ - width: '100%', padding: '15px', borderRadius: '8px', border: 'none', - background: '#1a1a2e', color: 'white', marginBottom: '30px', fontSize: '1rem' - }} - /> -
- {filtered.map(mem => ( -
-

{mem.title}

-

{mem.preview}

- {mem.date} -
- ))} -
-
- ); -} - // ============ TEAM ============ function Team() { const members: TeamMember[] = [ { id: '1', name: 'Jelena', role: 'Chief of Staff', status: 'working' }, { id: '2', name: 'Neo', role: 'CTO / DevOps', status: 'working' }, - { id: '3', name: 'Agent 3', role: 'Sales Agent', status: 'idle' }, - { id: '4', name: 'Agent 4', role: 'Ad Manager', status: 'idle' }, ]; return ( @@ -233,30 +185,44 @@ function Team() { ); } -// ============ OFFICE ============ -function Office() { - const agents = [ - { name: 'Jelena', area: 'Executive Suite', working: true, task: 'Managing operations' }, - { name: 'Neo', area: 'Server Room', working: true, task: 'Infrastructure monitoring' }, - ]; +// ============ MEMORY ============ +function Memory() { + const [search, setSearch] = useState(''); + + const [memories, setMemories] = useState([]); + + useEffect(() => { + fetch('/api/data?type=memory') + .then(r => r.json()) + .then(setMemories) + .catch(() => {}); + }, []); + + const filtered = memories.filter(m => m.title.toLowerCase().includes(search.toLowerCase())); return ( -
-

🏢 Digital Office

-
- {agents.map(agent => ( -
-
-

{agent.name}

- -
-

📍 {agent.area}

-

💻 {agent.task}

+
+ setSearch(e.target.value)} + style={{ + width: '100%', padding: '15px', borderRadius: '8px', border: 'none', + background: '#1a1a2e', color: 'white', marginBottom: '30px', fontSize: '1rem' + }} + /> +
+ {filtered.map(mem => ( +
+

{mem.title}

+

{mem.preview}

+ {mem.date}
))} + {filtered.length === 0 && ( +

No memories found

+ )}
);