# Subtask A: Add Legacy Indicators to LauncherProjectCard ## Goal Add visual indicators to project cards in the new launcher showing when a project is using the legacy React 17 runtime, with expandable details and action buttons. ## Files to Modify ### 1. `LauncherProjectCard.tsx` **Location**: `packages/noodl-core-ui/src/preview/launcher/Launcher/components/LauncherProjectCard/LauncherProjectCard.tsx` **Changes**: 1. Add `runtimeInfo` to the props interface 2. Detect if project is legacy based on runtime info 3. Add legacy warning badge 4. Add expandable legacy warning section with actions **Implementation**: ```typescript import { RuntimeVersionInfo } from '@noodl-types/migration'; // Add this import export interface LauncherProjectCardProps extends LauncherProjectData { contextMenuItems: ContextMenuProps[]; onClick?: () => void; runtimeInfo?: RuntimeVersionInfo; // NEW: Add runtime detection info onMigrateProject?: () => void; // NEW: Callback for migration onOpenReadOnly?: () => void; // NEW: Callback for read-only mode } export function LauncherProjectCard({ id, title, cloudSyncMeta, localPath, lastOpened, pullAmount, pushAmount, uncommittedChangesAmount, imageSrc, contextMenuItems, contributors, onClick, runtimeInfo, // NEW onMigrateProject, // NEW onOpenReadOnly // NEW }: LauncherProjectCardProps) { const { tags, getProjectMeta } = useProjectOrganization(); const [showLegacyDetails, setShowLegacyDetails] = useState(false); // Get project tags const projectMeta = getProjectMeta(localPath); const projectTags = projectMeta ? tags.filter((tag) => projectMeta.tagIds.includes(tag.id)) : []; // Determine if this is a legacy project const isLegacy = runtimeInfo?.version === 'react17'; const isDetecting = runtimeInfo === undefined; return (
{title} {/* NEW: Legacy warning icon */} {isLegacy && ( )} {/* NEW: Detection in progress */} {isDetecting && ( )} {/* Tags */} {projectTags.length > 0 && ( {projectTags.map((tag) => ( ))} )}
{/* Cloud sync column - unchanged */}
{/* ... existing cloud sync code ... */}
{/* Contributors column - unchanged */} {/* ... existing contributors code ... */}
{/* NEW: Legacy warning banner */} {isLegacy && (
React 17 (Legacy Runtime) { e.stopPropagation(); setShowLegacyDetails(!showLegacyDetails); }} />
)} {/* NEW: Expanded legacy details */} {isLegacy && showLegacyDetails && (
This project needs migration to work with OpenNoodl 1.2+. Your original project will remain untouched. { e.stopPropagation(); onMigrateProject?.(); }} /> { e.stopPropagation(); onOpenReadOnly?.(); }} /> { e.stopPropagation(); // Open documentation window.open('https://docs.opennoodl.com/migration', '_blank'); }} />
)}
); } ``` ### 2. `LauncherProjectCard.module.scss` **Location**: `packages/noodl-core-ui/src/preview/launcher/Launcher/components/LauncherProjectCard/LauncherProjectCard.module.scss` **Add these styles**: ```scss .LegacyCard { border-color: var(--theme-color-border-danger) !important; &:hover { border-color: var(--theme-color-border-danger-hover) !important; } } .LegacyBanner { display: flex; align-items: center; justify-content: space-between; gap: var(--spacing-2); margin-top: var(--spacing-3); padding: var(--spacing-2) var(--spacing-3); background-color: var(--theme-color-bg-danger-subtle); border: 1px solid var(--theme-color-border-danger); border-radius: var(--border-radius-medium); } .LegacyDetails { margin-top: var(--spacing-2); padding: var(--spacing-3); background-color: var(--theme-color-bg-2); border-radius: var(--border-radius-medium); border: 1px solid var(--theme-color-border-default); } ``` ### 3. `Projects.tsx` **Location**: `packages/noodl-core-ui/src/preview/launcher/Launcher/views/Projects.tsx` **Changes**: Pass runtime info and callbacks to each `LauncherProjectCard`: ```typescript import { LocalProjectsModel } from '@noodl-utils/LocalProjectsModel'; // Add import function Projects() { const { projects, selectedFolder, searchQuery, onMigrateProject, // NEW: From context onOpenProjectReadOnly // NEW: From context } = useLauncherContext(); // Get projects with runtime info const projectsWithRuntime = LocalProjectsModel.instance.getProjectsWithRuntime(); // Filter projects based on folder and search const filteredProjects = projectsWithRuntime .filter((project) => { if (selectedFolder && selectedFolder !== 'all') { const meta = getProjectMeta(project.localPath); return meta?.folderId === selectedFolder; } return true; }) .filter((project) => { if (!searchQuery) return true; return project.title.toLowerCase().includes(searchQuery.toLowerCase()); }); return (
{filteredProjects.map((project) => ( onMigrateProject(project)} // NEW onOpenReadOnly={() => onOpenProjectReadOnly(project)} // NEW contextMenuItems={ [ // ... existing context menu items ... ] } /> ))}
); } ``` ## Types to Add Create a new types file for migration if it doesn't exist: **File**: `packages/noodl-types/src/migration.ts` ```typescript export interface RuntimeVersionInfo { version: 'react17' | 'react19' | 'unknown'; confidence: 'high' | 'medium' | 'low'; indicators: string[]; } ``` ## Testing Checklist - [ ] Legacy projects show warning icon in title - [ ] Legacy projects have orange/red border - [ ] Legacy banner shows "React 17 (Legacy Runtime)" - [ ] Clicking "More" expands details section - [ ] Clicking "Less" collapses details section - [ ] "Migrate Project" button is visible - [ ] "View Read-Only" button is visible - [ ] "Learn More" button is visible - [ ] Normal projects don't show any legacy indicators - [ ] Detection spinner shows while runtime is being detected - [ ] Clicking card body for legacy projects doesn't trigger onClick ## Visual Design ``` ┌────────────────────────────────────────────────────────────┐ │ [Thumbnail] My Legacy Project ⚠️ │ │ Last opened 2 days ago │ │ │ │ ┌────────────────────────────────────────────┐│ │ │ ⚠️ React 17 (Legacy Runtime) [More ▼]││ │ └────────────────────────────────────────────┘│ │ │ │ ┌────────────────────────────────────────────┐│ │ │ This project needs migration to work with ││ │ │ OpenNoodl 1.2+. Your original project will ││ │ │ remain untouched. ││ │ │ ││ │ │ [Migrate Project] [View Read-Only] Learn More → ││ │ └────────────────────────────────────────────┘│ └────────────────────────────────────────────────────────────┘ ``` ## Notes - **Non-blocking**: Normal click behavior is disabled for legacy projects to prevent accidental opening - **Informative**: Clear warning with explanation - **Actionable**: Three clear paths forward (migrate, view, learn) - **Expandable**: Details hidden by default to avoid clutter - **Color coding**: Use danger colors to indicate incompatibility without being alarming ## Next Steps After completing this subtask: 1. Verify legacy badges appear correctly 2. Test expand/collapse behavior 3. Move to Subtask B to wire up the button callbacks