import React, { useEffect, useState } from 'react'
import { listDirectives, DirectiveSummary } from '../../services/directiveApi'
interface DirectiveFileTreeProps {
selectedDirectiveId: string | null
onSelectDirective: (id: string) => void
onNewDirective: () => void
}
interface GroupState {
[key: string]: boolean
}
const STATUS_GROUPS = [
{ key: 'active', label: 'Active', defaultExpanded: true },
{ key: 'idle', label: 'Idle', defaultExpanded: true },
{ key: 'draft', label: 'Draft', defaultExpanded: false },
{ key: 'archived', label: 'Archived', defaultExpanded: false },
] as const
function statusColor(status: string): string {
switch (status.toLowerCase()) {
case 'active':
case 'running':
return '#4caf50'
case 'idle':
case 'paused':
return '#ffc107'
case 'draft':
case 'pending':
return '#9e9e9e'
case 'archived':
case 'failed':
return '#f44336'
default:
return '#9e9e9e'
}
}
function groupDirectives(directives: DirectiveSummary[]): Record<string, DirectiveSummary[]> {
const groups: Record<string, DirectiveSummary[]> = {
active: [],
idle: [],
draft: [],
archived: [],
}
for (const d of directives) {
const s = d.status.toLowerCase()
if (s === 'active' || s === 'running') {
groups.active.push(d)
} else if (s === 'idle' || s === 'paused') {
groups.idle.push(d)
} else if (s === 'draft' || s === 'pending') {
groups.draft.push(d)
} else {
groups.archived.push(d)
}
}
return groups
}
export function DirectiveFileTree({ selectedDirectiveId, onSelectDirective, onNewDirective }: DirectiveFileTreeProps) {
const [directives, setDirectives] = useState<DirectiveSummary[]>([])
const [loading, setLoading] = useState(true)
const [error, setError] = useState<string | null>(null)
const [expanded, setExpanded] = useState<GroupState>(() => {
const state: GroupState = {}
for (const g of STATUS_GROUPS) {
state[g.key] = g.defaultExpanded
}
return state
})
useEffect(() => {
async function load() {
try {
setLoading(true)
const data = await listDirectives()
setDirectives(data)
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to load directives')
} finally {
setLoading(false)
}
}
load()
}, [])
const toggleGroup = (key: string) => {
setExpanded(prev => ({ ...prev, [key]: !prev[key] }))
}
const grouped = groupDirectives(directives)
return (
<div className="directive-file-tree">
<div className="file-tree-header">
<span className="file-tree-title">Directives</span>
<button className="file-tree-new-btn" onClick={onNewDirective} title="New Directive">
+
</button>
</div>
{loading && <div className="file-tree-loading">Loading...</div>}
{error && <div className="file-tree-error">{error}</div>}
{!loading && !error && (
<div className="file-tree-groups">
{STATUS_GROUPS.map(group => {
const items = grouped[group.key]
if (!items || items.length === 0) return null
return (
<div key={group.key} className="file-tree-group">
<button
className="file-tree-group-header"
onClick={() => toggleGroup(group.key)}
>
<span className={`file-tree-chevron ${expanded[group.key] ? 'expanded' : ''}`}>
{'\u25B6'}
</span>
<span className="file-tree-group-label">{group.label}</span>
<span className="file-tree-group-count">{items.length}</span>
</button>
{expanded[group.key] && (
<div className="file-tree-items">
{items.map(directive => (
<button
key={directive.id}
className={`file-tree-item ${selectedDirectiveId === directive.id ? 'selected' : ''}`}
onClick={() => onSelectDirective(directive.id)}
title={directive.title}
>
<span
className="file-tree-status-dot"
style={{ backgroundColor: statusColor(directive.status) }}
/>
<span className="file-tree-doc-icon">{'\u{1F4C4}'}</span>
<span className="file-tree-item-title">{directive.title || 'Untitled'}</span>
</button>
))}
</div>
)}
</div>
)
})}
</div>
)}
</div>
)
}