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}
)}
);
}
/**
* 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 && (
)}
)}
);
}
/**
* 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) => (
))}
)}
);
}