Files
metabuilder/docs/implementation/ui/components/COMPONENT_VIOLATION_ANALYSIS.md

17 KiB

📊 Component Size Violation Analysis & Refactoring Guide

Document Date: December 25, 2025
Total Violations: 8 components, 4,146 LOC over limit
Target State: 0 violations, 325 LOC total


📈 Complete Violation List

Tier 1: Critical (800+ LOC)

# Component Path Current Target Reduction Priority
1 GitHubActionsFetcher src/components/ 887 95 89% ↓ 🔴 P0
2 NerdModeIDE src/components/ 733 110 85% ↓ 🔴 P0
3 LuaEditor src/components/ 686 120 82% ↓ 🔴 P0

Tier 2: High (500-700 LOC)

# Component Path Current Target Reduction Priority
4 SchemaEditor src/components/ 520 100 80% ↓ 🟡 P1
5 ComponentConfigDialog src/components/ 450 90 80% ↓ 🟡 P1
6 PropertyInspector src/components/ 380 85 77% ↓ 🟡 P1

Tier 3: Medium (200-400 LOC)

# Component Path Current Target Reduction Priority
7 IRCWebchat src/components/ 290 85 70% ↓ 🟢 P2
8 AuditLogViewer src/components/ 210 70 66% ↓ 🟢 P2

Totals:

  • Before: 4,156 LOC across 8 files
  • After: 755 LOC across 40+ smaller files
  • Expected New Components: 32+ child components + 8+ hooks
  • Overall Reduction: 81.8% ↓

🔴 TIER 1 DETAILED ANALYSIS

Component 1: GitHubActionsFetcher.tsx (887 LOC)

Current Architecture:

┌─ GitHubActionsFetcher (887 LOC, monolithic)
├─ useState:
│  ├─ runs (workflow runs)
│  ├─ loading
│  ├─ error
│  ├─ filters (status, branch)
│  ├─ sortBy
│  ├─ autoRefresh
│  ├─ refreshInterval
│  └─ selectedRun
├─ Effects:
│  ├─ Initial fetch
│  ├─ Auto-refresh interval
│  ├─ Local storage sync
│  └─ Search debounce
├─ API Calls:
│  ├─ fetchWorkflowRuns()
│  ├─ downloadRunLogs()
│  ├─ retryFailedRun()
│  └─ analyzeRunLogs()
└─ Render:
   ├─ Filters UI
   ├─ Runs table (200+ LOC)
   ├─ Run details modal
   ├─ Analysis panel
   └─ Download progress

Issues:

  1. Mixed responsibilities: API integration + UI rendering + state management
  2. Hardcoded logic: Analysis features, filtering, sorting all in JSX
  3. No reusability: Hooks can't be used elsewhere
  4. Hard to test: Too many moving parts, complex mocking needed
  5. Performance: Re-renders entire tree on any state change

Refactoring Strategy:

BEFORE:
GitHubActionsFetcher.tsx (887 LOC)
  ├─ API integration (200 LOC)
  ├─ State management (150 LOC)
  ├─ Analysis logic (100 LOC)
  └─ Rendering (437 LOC)

AFTER:
useGitHubFetcher.ts (65 LOC)          ← API + polling
  ├─ Fetch runs
  ├─ Cache + refetch
  └─ Auto-refresh logic

useAnalyzeRuns.ts (45 LOC)            ← Analysis logic
  ├─ Analyze single run
  ├─ Analyze logs
  └─ Generate insights

WorkflowRunCard.tsx (48 LOC)           ← Single run display
  ├─ Run info
  ├─ Status badge
  └─ Quick actions

WorkflowFilterBar.tsx (42 LOC)         ← Filters
  ├─ Status filter
  ├─ Branch filter
  └─ Sort dropdown

WorkflowRunsTable.tsx (65 LOC)         ← Table view
  ├─ Column headers
  ├─ Row rendering (uses WorkflowRunCard)
  └─ Pagination

WorkflowAnalysisPanel.tsx (52 LOC)     ← Analysis UI
  ├─ Performance insights
  ├─ Job breakdown
  └─ Error summary

GitHubActionsFetcher.tsx (85 LOC)      ← Container
  ├─ Wire hooks
  ├─ Compose components
  └─ Handle interactions

Implementation Checklist:

Step 1: Create useGitHubFetcher Hook (90 min)

// src/hooks/useGitHubFetcher.ts
export interface UseGitHubFetcherOptions {
  autoRefresh?: boolean;
  refreshInterval?: number;
  filters?: WorkflowFilter;
}

export function useGitHubFetcher(options: UseGitHubFetcherOptions) {
  return {
    runs: WorkflowRun[],
    loading: boolean,
    error: Error | null,
    refresh: () => Promise<void>,
    setFilters: (filters: WorkflowFilter) => void,
    setSortBy: (field: string, direction: 'asc' | 'desc') => void,
  }
}

Responsibilities:

  • Fetch runs from GitHub API
  • Handle auth and errors
  • Manage auto-refresh interval
  • Cache results with stale-while-revalidate
  • Expose refresh and filter controls

Tests:

  • Test successful fetch
  • Test auth errors
  • Test network errors
  • Test auto-refresh interval
  • Test filter application
  • Test cache invalidation

Step 2: Create useAnalyzeRuns Hook (60 min)

// src/hooks/useAnalyzeRuns.ts
export interface AnalysisResult {
  totalRuns: number;
  successRate: number;
  averageDuration: number;
  failureReasons: Map<string, number>;
  slowestJobs: Job[];
}

export function useAnalyzeRuns() {
  return {
    analyze: (runs: WorkflowRun[]) => AnalysisResult,
    analyzeRun: (run: WorkflowRun) => RunAnalysis,
    analyzeLogs: (logs: string) => LogAnalysis,
  }
}

Tests:

  • Test success rate calculation
  • Test duration averaging
  • Test failure categorization
  • Test slowest job detection

Step 3: Create WorkflowRunCard Component (45 min)

// src/components/WorkflowRunCard.tsx (48 LOC)
interface WorkflowRunCardProps {
  run: WorkflowRun;
  onSelect: (run: WorkflowRun) => void;
  onRetry: (run: WorkflowRun) => void;
  onDownloadLogs: (run: WorkflowRun) => void;
}

export function WorkflowRunCard({
  run,
  onSelect,
  onRetry,
  onDownloadLogs,
}: WorkflowRunCardProps) {
  return (
    <div className="run-card">
      <div className="status-badge" data-status={run.status}>
        {run.status}
      </div>
      <div className="info">
        <h3>{run.name}</h3>
        <p>{run.branch}  {formatDate(run.createdAt)}</p>
      </div>
      <div className="duration">
        {formatDuration(run.duration)}
      </div>
      <div className="actions">
        <button onClick={() => onSelect(run)}>Details</button>
        {run.status === 'failed' && (
          <button onClick={() => onRetry(run)}>Retry</button>
        )}
        <button onClick={() => onDownloadLogs(run)}>Logs</button>
      </div>
    </div>
  );
}

Tests:

  • Test card rendering with different statuses
  • Test button clicks
  • Test conditional rendering (retry button)

Step 4-6: Create Other Components (2 hours each)

  • WorkflowFilterBar: Status/branch filters + sort
  • WorkflowRunsTable: List of WorkflowRunCard components
  • WorkflowAnalysisPanel: Statistics from useAnalyzeRuns

Step 7: Refactor Container Component (60 min)

// src/components/GitHubActionsFetcher.tsx (85 LOC)
export function GitHubActionsFetcher() {
  const { runs, loading, error, refresh, setFilters, setSortBy } =
    useGitHubFetcher({
      autoRefresh: true,
      refreshInterval: 30000,
    });

  const { analyze } = useAnalyzeRuns();
  const analysis = useMemo(() => analyze(runs), [runs]);

  const [selectedRun, setSelectedRun] = useState<WorkflowRun | null>(null);

  if (loading) return <LoadingSpinner />;
  if (error) return <ErrorBanner error={error} />;

  return (
    <div className="github-actions">
      <WorkflowFilterBar
        onFilterChange={setFilters}
        onSortChange={setSortBy}
      />
      <WorkflowRunsTable
        runs={runs}
        onSelectRun={setSelectedRun}
        onRetryRun={handleRetry}
        onDownloadLogs={handleDownloadLogs}
      />
      {selectedRun && (
        <WorkflowAnalysisPanel run={selectedRun} analysis={analysis} />
      )}
    </div>
  );
}

Keep API same: Consumers of GitHubActionsFetcher shouldn't notice any change.


Step 8: Add Tests (90 min)

// src/hooks/useGitHubFetcher.test.ts
describe('useGitHubFetcher', () => {
  it('should fetch workflow runs on mount', async () => {
    // Test setup with mock API
    // Verify hook returns runs
  });

  it('should handle authentication errors', async () => {
    // Mock API error
    // Verify error is returned
  });

  it('should auto-refresh at specified interval', async () => {
    // Mock timer
    // Verify refetch occurs
  });

  // ... 8+ more tests
});

// src/components/WorkflowRunCard.test.tsx
describe('WorkflowRunCard', () => {
  it('should render run information', () => {
    // Render component with mock run
    // Verify all fields displayed
  });

  it('should call onSelect when clicked', () => {
    // Click Details button
    // Verify onSelect called
  });

  // ... 4+ more tests
});

Effort Summary:

Task Effort Cumulative
useGitHubFetcher hook 90 min 90 min
useAnalyzeRuns hook 60 min 150 min
WorkflowRunCard 45 min 195 min
WorkflowFilterBar 45 min 240 min
WorkflowRunsTable 60 min 300 min
WorkflowAnalysisPanel 55 min 355 min
Container refactor 60 min 415 min
Tests 90 min 505 min
Total: 505 min (8.4 hours)

Component 2: NerdModeIDE.tsx (733 LOC)

Current Issues:

  • File tree navigation + code editor + console + git all combined
  • Complex state: expandedNodes, openFiles, activeTab, editorContent, unsavedChanges
  • Keyboard shortcuts hardcoded
  • Theme/settings not configurable

Refactoring Plan:

BEFORE: NerdModeIDE.tsx (733 LOC)
  ├─ File tree logic
  ├─ Editor state
  ├─ Console management
  └─ Git integration

AFTER:
useFileTree.ts (85 LOC)               ← Tree operations
useCodeEditor.ts (68 LOC)             ← Editor state
useConsoleOutput.ts (42 LOC)          ← Console management
useGitStatus.ts (38 LOC)              ← Git info

FileTreePanel.tsx (72 LOC)            ← Tree UI
EditorPanel.tsx (80 LOC)              ← Editor UI
ConsoleOutput.tsx (55 LOC)            ← Console UI
GitStatusBar.tsx (35 LOC)             ← Git UI

NerdModeIDE.tsx (110 LOC)             ← Container

Key Hooks:

// src/hooks/useFileTree.ts
interface FileTreeState {
  expandedNodes: Set<string>;
  selectedFile: string | null;
  searchTerm: string;
}

export function useFileTree(projectRoot: string) {
  return {
    tree: FileNode[],
    state: FileTreeState,
    toggleNode: (nodeId: string) => void,
    selectFile: (path: string) => void,
    createFile: (path: string) => Promise<void>,
    deleteFile: (path: string) => Promise<void>,
    renameFile: (oldPath: string, newPath: string) => Promise<void>,
  }
}

// src/hooks/useCodeEditor.ts
export function useCodeEditor(language: string) {
  return {
    content: string,
    setContent: (content: string) => void,
    openTabs: EditorTab[],
    activeTab: EditorTab | null,
    switchTab: (tabId: string) => void,
    closeTab: (tabId: string) => void,
    hasUnsavedChanges: boolean,
    save: () => Promise<void>,
  }
}

Effort: 25 hours (including all components and tests)


Component 3: LuaEditor.tsx (686 LOC)

Current Issues:

  • Script editing + preview + console combined
  • Hardcoded syntax highlighting
  • No script library integration
  • Limited error reporting

Refactoring Plan:

BEFORE: LuaEditor.tsx (686 LOC)
  ├─ Script editing
  ├─ Preview/execution
  └─ Console output

AFTER:
useLuaScript.ts (55 LOC)              ← Script CRUD
useLuaPreview.ts (48 LOC)             ← Execution

ScriptEditor.tsx (65 LOC)             ← Edit UI
ScriptPreview.tsx (48 LOC)            ← Preview UI

LuaEditor.tsx (120 LOC)               ← Container

Key Hooks:

// src/hooks/useLuaScript.ts
export function useLuaScript(scriptId?: string) {
  return {
    content: string,
    setContent: (content: string) => void,
    save: () => Promise<void>,
    metadata: ScriptMetadata,
    updateMetadata: (metadata: Partial<ScriptMetadata>) => void,
  }
}

// src/hooks/useLuaPreview.ts
export function useLuaPreview(script: string) {
  return {
    output: string,
    errors: LuaError[],
    run: (context: Record<string, any>) => Promise<void>,
    clear: () => void,
  }
}

Effort: 20 hours


🟡 TIER 2 ANALYSIS (Medium Components)

Component 4: SchemaEditor.tsx (520 LOC)

Issues: Form builder + schema display + validation all combined

Refactoring: Extract form builder to hooks, split into:

  • SchemaForm.tsx (80 LOC)
  • SchemaPreview.tsx (70 LOC)
  • useSchemaBuilder.ts (90 LOC)
  • Container (60 LOC)

Effort: 10 hours


Component 5: ComponentConfigDialog.tsx (450 LOC)

Issues: Too many tabs and config sections

Refactoring: Each config section as separate tab component

  • TabSelector.tsx (40 LOC)
  • GeneralTab.tsx (60 LOC)
  • StylesTab.tsx (70 LOC)
  • PropsTab.tsx (65 LOC)
  • Container (90 LOC)

Effort: 8 hours


Component 6: PropertyInspector.tsx (380 LOC)

Issues: Different prop types rendered in single component

Refactoring: Prop type components

  • PropertyInput.tsx (45 LOC)
  • PropertySelect.tsx (40 LOC)
  • PropertyColorPicker.tsx (35 LOC)
  • PropertyToggle.tsx (30 LOC)
  • Container (75 LOC)

Effort: 7 hours


🟢 TIER 3 ANALYSIS (Smaller Components)

Component 7: IRCWebchat.tsx (290 LOC)

Issues: Chat UI + message rendering + input handling

Refactoring:

  • MessageList.tsx (65 LOC)
  • ChatInput.tsx (45 LOC)
  • useChatMessages.ts (50 LOC)
  • Container (60 LOC)

Effort: 4 hours


Component 8: AuditLogViewer.tsx (210 LOC)

Issues: Table view + filtering + search

Refactoring:

  • AuditLogTable.tsx (85 LOC)
  • AuditLogFilter.tsx (50 LOC)
  • Container (50 LOC)

Effort: 3 hours


📊 Summary Table

Component Current Target Components Hooks Effort Start
GitHubActionsFetcher 887 85 6 2 8.4h Week 1
NerdModeIDE 733 110 4 4 25h Week 1
LuaEditor 686 120 2 2 20h Week 2
SchemaEditor 520 100 3 1 10h Week 2
ComponentConfigDialog 450 90 5 0 8h Week 2
PropertyInspector 380 85 4 0 7h Week 3
IRCWebchat 290 85 2 1 4h Week 3
AuditLogViewer 210 70 1 0 3h Week 3
TOTALS 4,156 755 32 10 85.4h

Testing Strategy

Unit Tests (Per Component)

  • Props validation
  • State management
  • Event handlers
  • Edge cases

Hook Tests

  • Initial state
  • State updates
  • Side effects
  • Error handling

Integration Tests

  • Component composition
  • Data flow through hierarchy
  • Performance (re-renders)

E2E Tests

  • Full workflows
  • Multi-component interactions
  • Performance under load

🎯 Success Criteria

Per Component

  • All child components < 150 LOC
  • All hooks < 100 LOC
  • Unit test coverage > 90%
  • No console warnings
  • Props fully documented with JSDoc
  • Accessibility compliant (WCAG 2.1 AA)
  • Performance: no regression in metrics

Overall

  • 0 violations remaining
  • 4,156 → 755 LOC reduction
  • 40+ small components + 10 reusable hooks
  • All tests passing
  • All code reviewed
  • Documentation complete

📋 Execution Checklist

Pre-Refactoring (Day 1)

  • Create feature branch
  • Set up test environment
  • Create Storybook stories (optional)
  • Document current API/props

Refactoring Phase

  • Create hooks
  • Create child components
  • Add unit tests
  • Refactor container
  • Update parent imports
  • Test integration

Post-Refactoring

  • Code review
  • E2E tests
  • Performance testing
  • Documentation
  • Merge to main

  1. GitHubActionsFetcher (8.4h) - Simpler, good learning example
  2. NerdModeIDE (25h) - Most complex, but infrastructure ready
  3. LuaEditor (20h) - Complex but fewer dependencies
  4. SchemaEditor (10h) - Medium complexity
  5. ComponentConfigDialog (8h) - Straightforward tab separation
  6. PropertyInspector (7h) - Similar patterns to PropertyInspector
  7. IRCWebchat (4h) - Simple message list pattern
  8. AuditLogViewer (3h) - Simple table pattern

Total Timeline: 8.5 weeks (1 developer, sequential)


🎓 Learning Resources

Component Decomposition Patterns

Hook Development

Testing


Generated: December 25, 2025
Next Review: After Phase 1A completion