import React, { useCallback, useEffect, useState } from 'react'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { ChevronDown } from 'lucide-react'; import { APP_VERSION } from '@/lib/version'; /** * Simple Collapsible Component (no external dependencies) */ function SimpleCollapsible({ defaultOpen = false, children, title }) { const [isOpen, setIsOpen] = useState(defaultOpen); return (
setIsOpen(!isOpen)} >
{title}
{isOpen && (
{children}
)}
); } // Priority mapping for color coding const PRIORITY_COLORS = { 'πŸ”΄': { bg: 'bg-red-500/10', border: 'border-l-4 border-red-500', text: 'text-red-600', label: 'CRITICAL' }, '🟠': { bg: 'bg-orange-500/10', border: 'border-l-4 border-orange-500', text: 'text-orange-600', label: 'HIGH' }, '🟑': { bg: 'bg-yellow-500/10', border: 'border-l-4 border-yellow-500', text: 'text-yellow-600', label: 'MEDIUM' }, 'πŸ”΅': { bg: 'bg-blue-500/10', border: 'border-l-4 border-blue-500', text: 'text-blue-600', label: 'LOW' }, 'πŸ’­': { bg: 'bg-gray-500/10', border: 'border-l-4 border-gray-500', text: 'text-gray-600', label: 'NICE TO HAVE' }, }; /** * Parse FUTURE.md content into structured roadmap items */ function parseFutureMarkdown(markdown) { const items = []; const lines = markdown.split('\n'); let currentPriority = null; let currentItem = null; for (let i = 0; i < lines.length; i++) { const line = lines[i].trim(); // Priority section header: ## πŸ”΄ CRITICAL if (line.startsWith('## πŸ”΄') || line.startsWith('## 🟠') || line.startsWith('## 🟑') || line.startsWith('## πŸ”΅') || line.startsWith('## πŸ’­')) { const match = line.match(/##\s+(πŸ”΄|🟠|🟑|πŸ”΅|πŸ’­)\s+(CRITICAL|HIGH|MEDIUM|LOW|NICE TO HAVE)/); if (match) { currentPriority = match[1]; } continue; } // Item header: ### πŸ”΄ Title β€” CRITICAL if (line.startsWith('### πŸ”΄') || line.startsWith('### 🟠') || line.startsWith('### 🟑') || line.startsWith('### πŸ”΅') || line.startsWith('### πŸ’­')) { if (currentItem) { items.push(currentItem); } const match = line.match(/###\s+(πŸ”΄|🟠|🟑|πŸ”΅|πŸ’­)\s+(.+?)\s*(β€”\s*(CRITICAL|HIGH|MEDIUM|LOW|NICE TO HAVE))?/); if (match) { currentItem = { priority: match[1], title: match[2].trim(), description: '', status: 'PENDING', added: '', addedBy: '', priorityLabel: match[4] || matchPriorityToLabel(match[1]) }; } continue; } // Parse item content if (currentItem && line) { if (line.startsWith('**Status:**')) { currentItem.status = line.replace('**Status:**', '').trim(); } else if (line.startsWith('**Added:**')) { const dateMatch = line.match(/(\d{4}-\d{2}-\d{2})/); if (dateMatch) { currentItem.added = dateMatch[1]; } const byMatch = line.match(/by\s+(.+)/); if (byMatch) { currentItem.addedBy = byMatch[1]; } } else if (!line.startsWith('**') || line.startsWith('**Description:**') || line.startsWith('**Rationale:**') || line.startsWith('**Implementation Notes:**')) { currentItem.description += line + '\n'; } } } if (currentItem) { items.push(currentItem); } return items; } /** * Map priority emoji to label */ function matchPriorityToLabel(emoji) { const mapping = { 'πŸ”΄': 'CRITICAL', '🟠': 'HIGH', '🟑': 'MEDIUM', 'πŸ”΅': 'LOW', 'πŸ’­': 'NICE TO HAVE' }; return mapping[emoji] || 'UNKNOWN'; } /** * Priority Badge Component */ function PriorityBadge({ emoji, label }) { const colors = PRIORITY_COLORS[emoji] || PRIORITY_COLORS['πŸ’­']; return ( {emoji} {label} ); } /** * Roadmap Card Component */ function RoadmapCard({ item }) { const colors = PRIORITY_COLORS[item.priority] || PRIORITY_COLORS['πŸ’­']; const isHighPriority = item.priority === 'πŸ”΄' || item.priority === '🟠'; return ( {item.title} }>
{item.status && ( Status: {item.status} )} {item.added && ( Added: {item.added} )} {item.addedBy && ( by {item.addedBy} )}
{item.description}
); } /** * Development Log Entry Component */ function DevLogEntry({ entry }) { const [isOpen, setIsOpen] = useState(false); return (
setIsOpen(!isOpen)} >
{entry.version} {entry.date}
{entry.status && ( {entry.status} )}
{isOpen && (
{entry.agents && entry.agents.length > 0 && (
{entry.agents.map((agent, idx) => ( {agent.status === 'COMPLETED' && 'βœ… '} {agent.name}: {agent.notes} ))}
)} {entry.filesModified && entry.filesModified.length > 0 && (

Files Modified:

{entry.filesModified.map((file, idx) => ( {file} ))}
)} {entry.details && (
{entry.details}
)}
)}
); } /** * Parse DEVELOPMENT_LOG.md content */ function parseDevLogMarkdown(markdown) { const entries = []; const sections = markdown.split('---'); for (const section of sections) { if (!section.trim()) continue; if (section.includes('Current Work') && !section.includes('Status:')) continue; if (section.includes('Completed Work') && !section.includes('Date:')) continue; const versionMatch = section.match(/v(\d+\.\d+\.\d+)/); const dateMatch = section.match(/(\d{4}-\d{2}-\d{2})/); if (versionMatch || dateMatch) { const entry = { version: versionMatch ? `v${versionMatch[1]}` : 'Unknown', date: dateMatch ? dateMatch[0] : 'Unknown', agents: [], filesModified: [], status: 'UNKNOWN', details: section.trim(), }; // Try to extract agent info from table-like format // Example: "Neo | βœ… COMPLETED | 1m 38s | Added `run()` functions..." const agentLines = section.split('\n').filter(line => line.includes('|') && (line.includes('βœ…') || line.includes('❌') || line.includes('⏳') || line.includes('⚠️')) ); for (const agentLine of agentLines) { const parts = agentLine.split('|').map(p => p.trim()); if (parts.length >= 4) { entry.agents.push({ name: parts[0], status: parts[1], time: parts[2], notes: parts.slice(3).join('|'), }); } } // Extract files modified const filesMatch = section.match(/Files Modified:\s*(.*)/); if (filesMatch) { entry.filesModified = filesMatch[1].split(',').map(f => f.trim()); } // Extract status from headers if (section.includes('COMPLETED')) { entry.status = 'COMPLETED'; } else if (section.includes('In Progress') || section.includes('IN PROGRESS')) { entry.status = 'IN PROGRESS'; } entries.push(entry); } } // Sort by date descending (most recent first) entries.sort((a, b) => { const dateA = new Date(a.date); const dateB = new Date(b.date); return dateB - dateA; }); return entries; } /** * Admin Dashboard Component */ export default function AdminDashboard({ about }) { const [roadmapItems, setRoadmapItems] = useState([]); const [devLogEntries, setDevLogEntries] = useState([]); const [loading, setLoading] = useState(true); const version = about?.version || APP_VERSION; const parseData = useCallback(() => { setLoading(true); try { if (about?.future) { const roadmap = parseFutureMarkdown(about.future); setRoadmapItems(roadmap); } if (about?.developmentLog) { const logs = parseDevLogMarkdown(about.developmentLog); setDevLogEntries(logs); } } finally { setLoading(false); } }, [about]); useEffect(() => { parseData(); }, [parseData]); if (loading) { return (
); } return (
{/* Version Badge */}
v{version}
{/* Roadmap Section */} πŸ—ΊοΈ Roadmap Current and upcoming features organized by priority
{roadmapItems.length === 0 ? (
No roadmap items found
) : (
{roadmapItems.map((item, idx) => ( ))}
)}
{/* Activity Log Section */} πŸ“ Development Activity Log Recent development work and completed tasks
{devLogEntries.length === 0 ? (
No activity log entries found
) : (
{devLogEntries.map((entry, idx) => ( ))}
)}
); }