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

670 lines
17 KiB
Markdown

# 📊 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)
```typescript
// 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)
```typescript
// 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)
```typescript
// 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)
```typescript
// 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)
```typescript
// 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:**
```typescript
// 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:**
```typescript
// 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
---
## 🚀 Recommended Refactoring Order
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
- [React docs: Composition vs Inheritance](https://react.dev/learn/thinking-in-react)
- [Atomic Design Methodology](https://bradfrost.com/blog/post/atomic-web-design/)
### Hook Development
- [React Hooks API Reference](https://react.dev/reference/react)
- [Separating Concerns with Hooks](https://react.dev/learn/separating-concerns-with-effects)
### Testing
- [React Testing Library](https://testing-library.com/docs/react-testing-library/intro)
- [Jest Documentation](https://jestjs.io/docs/getting-started)
---
**Generated:** December 25, 2025
**Next Review:** After Phase 1A completion