# Session 2: Post-Migration UX Features - Implementation Plan ## Status: Infrastructure Complete, UI Integration Pending ### Completed ✅ 1. **MigrationNotesManager.ts** - Complete helper system - `getMigrationNote(componentId)` - Get notes for a component - `getAllMigrationNotes(filter, includeDismissed)` - Get filtered notes - `getMigrationNoteCounts()` - Get counts by category - `dismissMigrationNote(componentId)` - Dismiss a note - Status/icon helper functions 2. **MigrationNotesPanel Component** - Complete React panel - Beautiful status-based UI with gradient headers - Shows issues, AI suggestions, help links - Dismiss functionality - Full styling in MigrationNotesPanel.module.scss 3. **Design System** - Consistent with Session 1 - Status colors: warning orange, AI purple, success green - Professional typography and spacing - Smooth animations and transitions ### Remaining Work 🚧 #### Part 2: Component Badges (2-3 hours) **Goal:** Add visual migration status badges to components in ComponentsPanel **Challenge:** ComponentsPanel.ts is a legacy jQuery-based view using underscore.js templates (not React) **Files to Modify:** 1. `packages/noodl-editor/src/editor/src/views/panels/componentspanel/ComponentsPanel.ts` 2. `packages/noodl-editor/src/editor/src/templates/componentspanel.html` 3. `packages/noodl-editor/src/editor/src/styles/componentspanel.css` **Implementation Steps:** **Step 2.1: Add migration data to component scopes** In `ComponentsPanel.ts`, in the `returnComponentScopeAndSetActive` function: ```typescript const returnComponentScopeAndSetActive = (c, f) => { const iconType = getComponentIconType(c); // Add migration note loading const migrationNote = getMigrationNote(c.fullName); const scope = { folder: f, comp: c, name: c.localName, isSelected: this.nodeGraphEditor?.getActiveComponent() === c, isPage: iconType === ComponentIconType.Page, isCloudFunction: iconType === ComponentIconType.CloudFunction, isRoot: ProjectModel.instance.getRootNode() && ProjectModel.instance.getRootNode().owner.owner == c, isVisual: iconType === ComponentIconType.Visual, isComponentFolder: false, canBecomeRoot: c.allowAsExportRoot, hasWarnings: WarningsModel.instance.hasComponentWarnings(c), // NEW: Migration data hasMigrationNote: Boolean(migrationNote && !migrationNote.dismissedAt), migrationStatus: migrationNote?.status || null, migrationNote: migrationNote }; // ... rest of function }; ``` **Step 2.2: Add badge click handler** Add this method to ComponentsPanelView class: ```typescript onComponentBadgeClicked(scope, el, evt) { evt.stopPropagation(); // Prevent component selection if (!scope.migrationNote) return; // Import at top: const { DialogLayerModel } = require('../../DialogLayer'); // Import at top: const { MigrationNotesPanel } = require('../MigrationNotesPanel'); const ReactDOM = require('react-dom/client'); const React = require('react'); const panel = React.createElement(MigrationNotesPanel, { component: scope.comp, note: scope.migrationNote, onClose: () => { DialogLayerModel.instance.hideDialog(); this.scheduleRender(); // Refresh to show dismissed state } }); DialogLayerModel.instance.showDialog({ content: panel, title: 'Migration Notes', width: 600 }); } ``` **Step 2.3: Update HTML template** In `componentspanel.html`, add badge markup to the `item` template after the warnings icon: ```html
``` **Step 2.4: Add badge CSS** In `componentspanel.css`: ```css /* Migration badges */ .components-panel-migration-badge { position: absolute; width: 16px; height: 16px; top: 8px; right: 0; border-radius: 50%; cursor: pointer; transition: transform var(--speed-turbo) var(--easing-base); opacity: 0.8; } .components-panel-migration-badge:hover { opacity: 1; transform: scale(1.1); } /* Badge colors by status */ .components-panel-migration-badge.badge-needs-review { background-color: #f59e0b; /* warning orange */ box-shadow: 0 0 6px rgba(245, 158, 11, 0.4); } .components-panel-migration-badge.badge-ai-migrated { background-color: #a855f7; /* AI purple */ box-shadow: 0 0 6px rgba(168, 85, 247, 0.4); } .components-panel-migration-badge.badge-auto { background-color: #10b981; /* success green */ box-shadow: 0 0 6px rgba(16, 185, 129, 0.4); } .components-panel-migration-badge.badge-manually-fixed { background-color: #10b981; /* success green */ box-shadow: 0 0 6px rgba(16, 185, 129, 0.4); } /* Selected state */ .components-panel-item-selected .components-panel-migration-badge { opacity: 1; } ``` #### Part 3: Filter System (2-3 hours) **Goal:** Add filter buttons to show/hide components by migration status **Step 3.1: Add filter state** In `ComponentsPanelView` class constructor: ```typescript constructor(args: ComponentsPanelOptions) { super(); // ... existing code ... // NEW: Migration filter state this.migrationFilter = 'all'; // 'all' | 'needs-review' | 'ai-migrated' | 'no-issues' } ``` **Step 3.2: Add filter methods** ```typescript setMigrationFilter(filter: MigrationFilter) { this.migrationFilter = filter; this.scheduleRender(); } shouldShowComponent(scope): boolean { // Always show if no filter if (this.migrationFilter === 'all') return true; const hasMigrationNote = scope.hasMigrationNote; const status = scope.migrationStatus; switch (this.migrationFilter) { case 'needs-review': return hasMigrationNote && status === 'needs-review'; case 'ai-migrated': return hasMigrationNote && status === 'ai-migrated'; case 'no-issues': return !hasMigrationNote; default: return true; } } ``` **Step 3.3: Apply filter in renderFolder** In the `renderFolder` method, wrap component rendering: ```typescript // Then component items for (var i in folder.components) { const c = folder.components[i]; const scope = returnComponentScopeAndSetActive(c, folder); // NEW: Apply filter if (!this.shouldShowComponent(scope)) continue; this.componentScopes[c.fullName] = scope; // ... rest of rendering ... } ``` **Step 3.4: Add filter UI to HTML template** Add after the Components header in `componentspanel.html`: ```html
``` **Step 3.5: Add filter CSS** ```css /* Migration filters */ .components-panel-filters { display: flex; gap: 4px; padding: 8px 10px; background-color: var(--theme-color-bg-2); border-bottom: 1px solid var(--theme-color-border-default); } .components-panel-filter-btn { flex: 1; padding: 6px 12px; font: 11px var(--font-family-regular); color: var(--theme-color-fg-default); background-color: var(--theme-color-bg-3); border: 1px solid var(--theme-color-border-default); border-radius: 4px; cursor: pointer; transition: all var(--speed-turbo) var(--easing-base); } .components-panel-filter-btn:hover { background-color: var(--theme-color-bg-4); color: var(--theme-color-fg-highlight); } .components-panel-filter-btn.is-active { background-color: var(--theme-color-secondary); color: var(--theme-color-on-secondary); border-color: var(--theme-color-secondary); } /* Badge-colored filters */ .components-panel-filter-btn.badge-needs-review.is-active { background-color: #f59e0b; border-color: #f59e0b; } .components-panel-filter-btn.badge-ai-migrated.is-active { background-color: #a855f7; border-color: #a855f7; } ``` ### Testing Checklist Before considering Session 2 complete: - [ ] Badges appear on migrated components - [ ] Badge colors match status (orange=needs-review, purple=AI, green=auto) - [ ] Clicking badge opens MigrationNotesPanel - [ ] Dismissing note removes badge - [ ] Filters show/hide correct components - [ ] Filter counts update correctly - [ ] Filter state persists during navigation - [ ] Selected component stays visible when filtering - [ ] No console errors - [ ] Performance is acceptable with many components ### Notes - **Legacy Code Warning:** ComponentsPanel uses jQuery + underscore.js templates, not React - **Import Pattern:** Uses `require()` statements for dependencies - **Rendering Pattern:** Uses `bindView()` with templates, not JSX - **Event Handling:** Uses `data-click` attributes, not React onClick - **State Management:** Uses plain object scopes, not React state ### Deferred Features - **Code Diff Viewer:** Postponed - not critical for initial release - Could be added later if users request it - Would require significant UI work for side-by-side diff - Current "AI Suggestions" text is sufficient --- **Next Steps:** Implement Part 2 (Badges) first, test thoroughly, then implement Part 3 (Filters).