summaryrefslogblamecommitdiff
path: root/frontend/src/components/document/DirectiveFileTree.tsx
blob: 21050ca259972923d3e20bc28e5241b27b6e0274 (plain) (tree)

























































































































































                                                                                                                       
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>
  )
}