# Refactoring Quick Reference Guide ## TL;DR Summary **Problem:** 8 React components exceed 150 LOC limit (violates MetaBuilder architecture) **Solution:** Decompose into smaller components, extract logic to hooks and Lua **Timeline:** 3 weeks, 103 hours of work **Impact:** Improved modularity, testability, and data persistence | Violation | Current | Target | Reduction | |-----------|---------|--------|-----------| | GitHubActionsFetcher | 887 LOC | 95 LOC | 89% ↓ | | NerdModeIDE | 733 LOC | 110 LOC | 85% ↓ | | LuaEditor | 686 LOC | 120 LOC | 82% ↓ | --- ## Key Architectural Changes ### 1. Component Decomposition Pattern **Before:** ``` MonolithicComponent.tsx (887 LOC) - API fetching - State management - UI rendering - Business logic - Error handling ``` **After:** ``` MonolithicComponent.tsx (95 LOC) ├── SubComponent1.tsx (65 LOC) - Handle one thing ├── SubComponent2.tsx (80 LOC) - Handle one thing ├── SubComponent3.tsx (75 LOC) - Handle one thing └── Hooks & Lua for business logic ``` ### 2. State Management Shift **Before:** ```typescript const [data, setData] = useState(null) const [isLoading, setIsLoading] = useState(false) const [error, setError] = useState(null) // ... 8+ more useState // Lost on page reload, no sharing ``` **After:** ```typescript // Database (Prisma) - Persistent model WorkflowRun { id: String name: String status: String // ... shared across sessions and users } // React State - Ephemeral UI state const [selectedId, setSelectedId] = useState(null) // UI only const { runs } = useWorkflowActions() // From database ``` ### 3. Logic Extraction Pattern **Before:** ```typescript // In component, 200+ LOC of async operations const fetchGitHubActions = async () => { // Set loading, call API, handle errors, cache... } ``` **After:** ```typescript // Custom hook - 50 LOC, testable, reusable export function useGitHubActions() { // All API logic, caching, error handling return { runs, isLoading, error, refetch } } // In component - 5 LOC const { runs, isLoading } = useGitHubActions() ``` ### 4. Configuration as Data **Before:** ```typescript const getStatusColor = (status: string) => { if (status === 'completed') { if (conclusion === 'success') return 'text-green-600' // ... 50 lines of hardcoded logic } } ``` **After (JSON Config):** ```json { "statusColors": { "completed-success": "text-green-600", "completed-failure": "text-red-600" } } ``` **Usage:** ```typescript const config = useUIConfig('GitHubActions') const color = config.statusColors[status] ``` --- ## Implementation Roadmap ### Week 1: Violation #1 (GitHubActionsFetcher) ``` Day 1-2: Database models + migrations Day 3-4: useGitHubActions hook Day 5: useWorkflowLogs + useWorkflowAnalysis hooks Day 6: Sub-components creation Day 7: Main component refactor + tests ``` ### Week 2: Violation #2 (NerdModeIDE) ``` Day 1: Database models (projects, files, git config) Day 2-4: useIDEFileTree (3 hours) + other hooks Day 5: File tree components Day 6: Editor + toolbar components Day 7: Main component refactor + tests ``` ### Week 3: Violation #3 (LuaEditor) ``` Day 1-2: Extract Monaco config + create hooks Day 3-4: Parameter + execution components Day 5: Security + snippet components Day 6: Main component refactor Day 7: Testing + documentation ``` --- ## File Structure After Refactoring ``` src/ ├── components/ │ ├── GitHubActionsFetcher.tsx (95 LOC) ← Refactored │ │ ├── GitHubActionsToolbar.tsx (65 LOC) │ │ ├── GitHubActionsRunsList.tsx (80 LOC) │ │ ├── GitHubActionsRunRow.tsx (45 LOC) │ │ ├── GitHubActionsJobsPanel.tsx (100 LOC) │ │ ├── GitHubActionsJobStep.tsx (50 LOC) │ │ └── GitHubActionsAnalysisPanel.tsx (70 LOC) │ │ │ ├── NerdModeIDE.tsx (110 LOC) ← Refactored │ │ ├── IDEFileTreePanel.tsx (90 LOC) │ │ ├── IDEFileTreeNode.tsx (60 LOC) │ │ ├── IDEEditorPanel.tsx (85 LOC) │ │ ├── IDEToolbar.tsx (65 LOC) │ │ ├── IDEConsolePanel.tsx (70 LOC) │ │ ├── IDEGitDialog.tsx (75 LOC) │ │ └── IDENewFileDialog.tsx (65 LOC) │ │ │ ├── LuaEditor.tsx (120 LOC) ← Refactored │ │ ├── LuaScriptSelector.tsx (70 LOC) │ │ ├── LuaCodeEditor.tsx (110 LOC) │ │ ├── LuaMonacoConfig.tsx (120 LOC) │ │ ├── LuaParametersPanel.tsx (95 LOC) │ │ ├── LuaParameterItem.tsx (60 LOC) │ │ ├── LuaExecutionPanel.tsx (100 LOC) │ │ ├── LuaExecutionResult.tsx (80 LOC) │ │ ├── LuaSecurityWarningDialog.tsx (75 LOC) │ │ └── LuaSnippetLibraryPanel.tsx (85 LOC) │ │ │ └── (existing components...) │ ├── hooks/ │ ├── useGitHubActions.ts (60 LOC) │ ├── useWorkflowLogs.ts (50 LOC) │ ├── useWorkflowAnalysis.ts (40 LOC) │ │ │ ├── useIDEProject.ts (40 LOC) │ ├── useIDEFileTree.ts (100 LOC) │ ├── useIDEEditor.ts (40 LOC) │ ├── useIDEGit.ts (60 LOC) │ ├── useIDETest.ts (50 LOC) │ │ │ ├── useLuaScripts.ts (40 LOC) │ ├── useLuaEditor.ts (40 LOC) │ ├── useLuaExecution.ts (60 LOC) │ ├── useLuaSecurity.ts (30 LOC) │ │ │ └── (existing hooks...) │ ├── lib/ │ ├── github-ui-config.ts (30 LOC) │ └── (existing libs...) │ └── seed-data/ └── lua-scripts/ ├── filter-workflow-runs.lua ├── aggregate-logs.lua └── (more scripts...) ``` --- ## Database Schema Summary ### New Models (15 total) **GitHub Feature:** - `GitHubWorkflowRun` - Workflow execution data - `GitHubWorkflowJob` - Job details - `GitHubConfig` - API config **IDE Feature:** - `IDEProject` - Project metadata - `IDEFile` - File tree with content - `GitConfig` - Git credentials (update) - `IDETestRun` - Test results **Lua Feature:** - `LuaScriptLibrary` - Reusable snippets - `LuaScriptParameter` - Parameter definitions - `LuaTestRun` - Execution history **Updated Models:** - `LuaScript` - Add description field - `User` - Track changes --- ## Hook API Patterns ### Pattern 1: Data Fetching Hook ```typescript export function useGitHubActions() { const [runs, setRuns] = useState([]) const [isLoading, setIsLoading] = useState(false) const [error, setError] = useState(null) const refetch = useCallback(async () => { setIsLoading(true) try { const data = await fetchFromAPI() setRuns(data) } catch (e) { setError(e.message) } finally { setIsLoading(false) } }, []) useEffect(() => { refetch() }, []) return { runs, isLoading, error, refetch } } ``` ### Pattern 2: State Management Hook ```typescript export function useIDEFileTree(projectId: string) { const [files, setFiles] = useState([]) const createFile = useCallback(async (parentId: string, name: string) => { const newFile = await db.file.create({ projectId, parentId, name }) setFiles([...files, newFile]) }, [files]) return { files, createFile, deleteFile, updateFile } } ``` ### Pattern 3: Derived State Hook ```typescript export function useLuaEditor(scripts: LuaScript[]) { const [selectedId, setSelectedId] = useState( scripts.length > 0 ? scripts[0].id : null ) const currentScript = useMemo( () => scripts.find(s => s.id === selectedId) || null, [scripts, selectedId] ) return { selectedId, setSelectedId, currentScript } } ``` --- ## Testing Strategy ### Unit Test Template ```typescript describe('useGitHubActions', () => { it('should fetch and cache workflow runs', async () => { const { result } = renderHook(() => useGitHubActions(), { wrapper: MockProviders }) expect(result.current.isLoading).toBe(true) await waitFor(() => { expect(result.current.runs.length).toBeGreaterThan(0) expect(result.current.isLoading).toBe(false) }) }) }) ``` ### Component Test Template ```typescript describe('GitHubActionsRunRow', () => { it('should render workflow run data', () => { const mockRun = { id: 1, name: 'Test', status: 'completed' } render( ) expect(screen.getByText('Test')).toBeInTheDocument() }) }) ``` --- ## Migration Path (No Breaking Changes) ### Step 1: Create New Infrastructure ```bash 1. Add new Prisma models 2. Create hooks (side-by-side with old code) 3. Create new sub-components 4. Create wrapper for backward compatibility ``` ### Step 2: Gradual Adoption ```bash 1. Tests use new hooks/components 2. Documentation shows new patterns 3. New features use new patterns 4. Old code still works ``` ### Step 3: Cleanup (Optional) ```bash 1. Remove old component after 1 release 2. Move any users to new version 3. Clean up deprecated code ``` --- ## Common Pitfalls to Avoid ### ❌ Don't: Props Drilling ```typescript // BAD - passing through 5 levels ``` ### ✅ Do: Use Context or Hooks ```typescript // GOOD - hook provides data const { data, onChange } = useWorkflowContext() ``` ### ❌ Don't: Mix Data and UI ```typescript // BAD - API calls in component export function MyComponent() { const [data, setData] = useState(null) useEffect(() => { fetch('/api/data').then(setData) // Should be in hook! }, []) } ``` ### ✅ Do: Separate Concerns ```typescript // GOOD - hook handles data export function useMyData() { const [data, setData] = useState(null) useEffect(() => { fetch('/api/data').then(setData) }, []) return data } export function MyComponent() { const data = useMyData() } ``` ### ❌ Don't: Hardcode UI Configuration ```typescript // BAD - colors hardcoded const statusColors = { success: '#16a34a', failure: '#dc2626' } ``` ### ✅ Do: Load from Database ```typescript // GOOD - config in database const config = useUIConfig('GitHubActions') const color = config.statusColors[status] ``` --- ## Success Metrics ### Before Refactoring - Average component size: 569 LOC - Components > 150 LOC: 8 - Custom hooks: ~5 - Lua scripts: ~10 ### After Refactoring - Average component size: 73 LOC - Components > 150 LOC: 0 ✅ - Custom hooks: ~20 ✅ - Lua scripts: ~15 ✅ --- ## Next Steps 1. **Review** this strategy with team 2. **Adjust** timeline based on capacity 3. **Start** with Phase 1 (GitHubActionsFetcher) 4. **Track** progress with checklist 5. **Celebrate** each milestone 6. **Repeat** for Violations #2 and #3 --- ## Questions to Review - [ ] Database schema approved? - [ ] Hook API contracts clear? - [ ] Component responsibility boundaries defined? - [ ] Testing approach agreed? - [ ] Timeline realistic? - [ ] Team capacity sufficient? - [ ] Risk mitigation plans ready? ## Contact / Escalation - **Questions about architecture?** → Review copilot-instructions.md - **Database schema issues?** → Check prisma/schema.prisma - **Hook design help?** → Look at existing hooks in src/hooks/ - **Component composition questions?** → Review /packages/ structure --- **Document Version:** 1.0 **Last Updated:** 2025-12-25 **Status:** Ready for implementation **Approval:** Pending ⏳