mirror of
https://github.com/The-Low-Code-Foundation/OpenNoodl.git
synced 2026-01-12 15:22:55 +01:00
464 lines
20 KiB
Markdown
464 lines
20 KiB
Markdown
# VIEW-006: Impact Radar
|
||
|
||
**View Type:** 🎨 Canvas Overlay (enhances existing canvas with highlighting)
|
||
|
||
## Overview
|
||
|
||
Before you change something, see everywhere it's used and what might break. A pre-change impact analysis tool that shows the blast radius of modifications. **Highlights persist on the canvas until dismissed!**
|
||
|
||
**Estimate:** 3-4 days
|
||
**Priority:** MEDIUM
|
||
**Complexity:** Medium
|
||
**Dependencies:** VIEW-000 (Foundation), PREREQ-004 (Canvas Highlighting API)
|
||
|
||
---
|
||
|
||
## The Problem
|
||
|
||
When you want to change something in a Noodl project:
|
||
- You don't know everywhere a component is used
|
||
- You don't know what depends on a specific output
|
||
- Changing a component's interface might break 5 other places
|
||
- Renaming a variable might affect components you forgot about
|
||
- "I'll just change this real quick" → breaks production
|
||
|
||
---
|
||
|
||
## The Solution
|
||
|
||
An impact analysis that:
|
||
1. Shows everywhere a component/node is used
|
||
2. Shows what depends on specific outputs
|
||
3. Warns about breaking changes
|
||
4. Lets you preview "what if I change this?"
|
||
5. Provides a checklist of things to update
|
||
|
||
---
|
||
|
||
## User Stories
|
||
|
||
1. **As a developer refactoring**, I want to know everywhere this component is used before I change its interface.
|
||
|
||
2. **As a developer renaming**, I want to see all places that reference this name so I don't break anything.
|
||
|
||
3. **As a developer removing**, I want to know if anything depends on this before I delete it.
|
||
|
||
4. **As a developer planning**, I want to understand the impact of a proposed change before implementing it.
|
||
|
||
---
|
||
|
||
## UI Design
|
||
|
||
### Component Impact View
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────┐
|
||
│ Impact Radar [Refresh] │
|
||
├─────────────────────────────────────────────────────────────────┤
|
||
│ Analyzing: AuthFlow Component │
|
||
├─────────────────────────────────────────────────────────────────┤
|
||
│ │
|
||
│ 📊 USAGE SUMMARY │
|
||
│ ┌─────────────────────────────────────────────────────────────┐ │
|
||
│ │ Used in 3 components across 2 pages │ │
|
||
│ │ │ │
|
||
│ │ Impact level: 🟡 MEDIUM │ │
|
||
│ │ Reason: Changes affect multiple pages │ │
|
||
│ └─────────────────────────────────────────────────────────────┘ │
|
||
│ │
|
||
│ 📍 USAGE LOCATIONS │
|
||
│ ┌─────────────────────────────────────────────────────────────┐ │
|
||
│ │ │ │
|
||
│ │ ┌─ Login Page ────────────────────────────────────────────┐ │ │
|
||
│ │ │ Instance: authFlow_1 [→ Jump] │ │ │
|
||
│ │ │ │ │ │
|
||
│ │ │ Connected inputs: │ │ │
|
||
│ │ │ • onLoginRequest ← LoginButton.onClick │ │ │
|
||
│ │ │ • redirectUrl ← "/dashboard" (static) │ │ │
|
||
│ │ │ │ │ │
|
||
│ │ │ Connected outputs: │ │ │
|
||
│ │ │ • currentUser → NavBar.user, ProfileWidget.user │ │ │
|
||
│ │ │ • onSuccess → Navigate("/dashboard") │ │ │
|
||
│ │ │ • authError → ErrorToast.message │ │ │
|
||
│ │ └─────────────────────────────────────────────────────────┘ │ │
|
||
│ │ │ │
|
||
│ │ ┌─ Settings Page ─────────────────────────────────────────┐ │ │
|
||
│ │ │ Instance: authFlow_2 [→ Jump] │ │ │
|
||
│ │ │ │ │ │
|
||
│ │ │ Connected inputs: │ │ │
|
||
│ │ │ • onLogoutRequest ← LogoutButton.onClick │ │ │
|
||
│ │ │ │ │ │
|
||
│ │ │ Connected outputs: │ │ │
|
||
│ │ │ • onSuccess → Navigate("/login") │ │ │
|
||
│ │ └─────────────────────────────────────────────────────────┘ │ │
|
||
│ │ │ │
|
||
│ │ ┌─ App Shell ─────────────────────────────────────────────┐ │ │
|
||
│ │ │ Instance: authFlow_global [→ Jump] │ │ │
|
||
│ │ │ │ │ │
|
||
│ │ │ Connected outputs: │ │ │
|
||
│ │ │ • isAuthenticated → RouteGuard.condition │ │ │
|
||
│ │ │ • currentUser → (passed to 12 child components) │ │ │
|
||
│ │ └─────────────────────────────────────────────────────────┘ │ │
|
||
│ └─────────────────────────────────────────────────────────────┘ │
|
||
│ │
|
||
│ ⚠️ CHANGE IMPACT ANALYSIS │
|
||
│ ┌─────────────────────────────────────────────────────────────┐ │
|
||
│ │ │ │
|
||
│ │ If you MODIFY these outputs: │ │
|
||
│ │ │ │
|
||
│ │ • currentUser Impact: 🔴 HIGH │ │
|
||
│ │ └─ Used by 14 consumers across 3 components │ │
|
||
│ │ └─ Breaking changes will affect: NavBar, ProfileWidget, │ │
|
||
│ │ UserSettings, ChatHeader, and 10 more... │ │
|
||
│ │ │ │
|
||
│ │ • onSuccess Impact: 🟡 MEDIUM │ │
|
||
│ │ └─ Used by 3 consumers │ │
|
||
│ │ └─ Navigation flows depend on this signal │ │
|
||
│ │ │ │
|
||
│ │ • authError Impact: 🟢 LOW │ │
|
||
│ │ └─ Used by 2 consumers │ │
|
||
│ │ └─ Only affects error display │ │
|
||
│ │ │ │
|
||
│ │ If you REMOVE these inputs: │ │
|
||
│ │ │ │
|
||
│ │ • redirectUrl Impact: 🟡 MEDIUM │ │
|
||
│ │ └─ Login Page passes static value │ │
|
||
│ │ └─ Would need to handle internally or change caller │ │
|
||
│ │ │ │
|
||
│ └─────────────────────────────────────────────────────────────┘ │
|
||
│ │
|
||
│ 📋 CHANGE CHECKLIST │
|
||
│ ┌─────────────────────────────────────────────────────────────┐ │
|
||
│ │ If changing currentUser output structure: │ │
|
||
│ │ □ Update Login Page → NavBar connection │ │
|
||
│ │ □ Update Login Page → ProfileWidget connection │ │
|
||
│ │ □ Update App Shell → RouteGuard connection │ │
|
||
│ │ □ Update App Shell → 12 child components │ │
|
||
│ │ □ Test login flow on Login Page │ │
|
||
│ │ □ Test auth guard on protected routes │ │
|
||
│ └─────────────────────────────────────────────────────────────┘ │
|
||
│ │
|
||
└─────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### Port-Specific Impact
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────┐
|
||
│ Impact Radar: currentUser output │
|
||
├─────────────────────────────────────────────────────────────────┤
|
||
│ │
|
||
│ This output feeds into: │
|
||
│ │
|
||
│ ┌─────────────────────┐ │
|
||
│ │ AuthFlow │ │
|
||
│ │ currentUser output │ │
|
||
│ └──────────┬──────────┘ │
|
||
│ │ │
|
||
│ ┌─────────────┼─────────────┬─────────────┐ │
|
||
│ ▼ ▼ ▼ ▼ │
|
||
│ ┌────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
||
│ │ NavBar │ │ Profile │ │ Settings │ │ +9 more │ │
|
||
│ │ .user │ │ Widget │ │ .user │ │ │ │
|
||
│ └────────┘ │ .user │ └──────────┘ └──────────┘ │
|
||
│ └──────────┘ │
|
||
│ │
|
||
│ Properties accessed: │
|
||
│ • .name (used in 8 places) │
|
||
│ • .email (used in 3 places) │
|
||
│ • .avatar (used in 4 places) │
|
||
│ • .role (used in 2 places) │
|
||
│ │
|
||
│ ⚠️ If you change the structure of currentUser: │
|
||
│ All 17 usages will need to be verified │
|
||
│ │
|
||
└─────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### Quick Impact Badge (for Components Panel)
|
||
|
||
```
|
||
┌────────────────────────────────────────┐
|
||
│ 🧩 AuthFlow (×3) 🔴 │
|
||
│ Used in 3 places, HIGH impact │
|
||
└────────────────────────────────────────┘
|
||
```
|
||
|
||
---
|
||
|
||
## Technical Design
|
||
|
||
### Data Model
|
||
|
||
```typescript
|
||
interface ImpactAnalysis {
|
||
target: {
|
||
type: 'component' | 'node' | 'port';
|
||
component?: ComponentModel;
|
||
node?: NodeGraphNode;
|
||
port?: string;
|
||
};
|
||
|
||
summary: {
|
||
usageCount: number;
|
||
impactLevel: 'low' | 'medium' | 'high' | 'critical';
|
||
reason: string;
|
||
};
|
||
|
||
usages: ComponentUsageDetail[];
|
||
|
||
portImpacts: PortImpact[];
|
||
|
||
changeChecklist: ChecklistItem[];
|
||
}
|
||
|
||
interface ComponentUsageDetail {
|
||
component: ComponentModel; // Where it's used
|
||
instanceNodeId: string; // The instance node
|
||
instanceLabel: string;
|
||
|
||
connectedInputs: {
|
||
port: string;
|
||
connections: {
|
||
fromNode: NodeGraphNode;
|
||
fromPort: string;
|
||
isStatic: boolean;
|
||
staticValue?: unknown;
|
||
}[];
|
||
}[];
|
||
|
||
connectedOutputs: {
|
||
port: string;
|
||
connections: {
|
||
toNode: NodeGraphNode;
|
||
toPort: string;
|
||
transitiveUsages?: number; // How many things depend on this downstream
|
||
}[];
|
||
}[];
|
||
}
|
||
|
||
interface PortImpact {
|
||
port: string;
|
||
direction: 'input' | 'output';
|
||
impactLevel: 'low' | 'medium' | 'high' | 'critical';
|
||
consumerCount: number;
|
||
consumers: {
|
||
component: string;
|
||
node: string;
|
||
port: string;
|
||
}[];
|
||
propertiesAccessed?: string[]; // For object outputs, what properties are used
|
||
}
|
||
|
||
interface ChecklistItem {
|
||
action: string;
|
||
component: string;
|
||
priority: 'required' | 'recommended' | 'optional';
|
||
completed: boolean;
|
||
}
|
||
```
|
||
|
||
### Building Impact Analysis
|
||
|
||
```typescript
|
||
function analyzeImpact(
|
||
project: ProjectModel,
|
||
target: ImpactTarget
|
||
): ImpactAnalysis {
|
||
if (target.type === 'component') {
|
||
return analyzeComponentImpact(project, target.component);
|
||
} else if (target.type === 'node') {
|
||
return analyzeNodeImpact(project, target.component, target.node);
|
||
} else {
|
||
return analyzePortImpact(project, target.component, target.node, target.port);
|
||
}
|
||
}
|
||
|
||
function analyzeComponentImpact(
|
||
project: ProjectModel,
|
||
component: ComponentModel
|
||
): ImpactAnalysis {
|
||
// Find all usages
|
||
const usages = findComponentUsages(project, component.fullName);
|
||
|
||
// Analyze each port
|
||
const portImpacts: PortImpact[] = [];
|
||
|
||
// Outputs - what depends on them?
|
||
component.getPorts().filter(p => p.plug === 'output').forEach(port => {
|
||
const consumers = findPortConsumers(project, component, port.name, usages);
|
||
portImpacts.push({
|
||
port: port.name,
|
||
direction: 'output',
|
||
impactLevel: calculateImpactLevel(consumers.length),
|
||
consumerCount: consumers.length,
|
||
consumers,
|
||
propertiesAccessed: analyzePropertyAccess(consumers)
|
||
});
|
||
});
|
||
|
||
// Inputs - what provides them?
|
||
component.getPorts().filter(p => p.plug === 'input').forEach(port => {
|
||
const providers = findPortProviders(project, component, port.name, usages);
|
||
portImpacts.push({
|
||
port: port.name,
|
||
direction: 'input',
|
||
impactLevel: calculateImpactLevel(providers.length),
|
||
consumerCount: providers.length,
|
||
consumers: providers
|
||
});
|
||
});
|
||
|
||
// Calculate overall impact
|
||
const maxImpact = Math.max(...portImpacts.map(p => impactScore(p.impactLevel)));
|
||
|
||
// Generate checklist
|
||
const checklist = generateChecklist(component, usages, portImpacts);
|
||
|
||
return {
|
||
target: { type: 'component', component },
|
||
summary: {
|
||
usageCount: usages.length,
|
||
impactLevel: scoreToLevel(maxImpact),
|
||
reason: generateImpactReason(usages, portImpacts)
|
||
},
|
||
usages: usages.map(u => buildUsageDetail(project, component, u)),
|
||
portImpacts,
|
||
changeChecklist: checklist
|
||
};
|
||
}
|
||
|
||
function calculateImpactLevel(consumerCount: number): ImpactLevel {
|
||
if (consumerCount === 0) return 'low';
|
||
if (consumerCount <= 2) return 'low';
|
||
if (consumerCount <= 5) return 'medium';
|
||
if (consumerCount <= 10) return 'high';
|
||
return 'critical';
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Implementation Phases
|
||
|
||
### Phase 1: Usage Finding (1 day)
|
||
|
||
1. Build on VIEW-000's `findComponentUsages()`
|
||
2. Add detailed connection analysis per usage
|
||
3. Track which ports are connected where
|
||
4. Count transitive dependencies
|
||
|
||
**Verification:**
|
||
- [ ] Finds all component usages
|
||
- [ ] Connection details accurate
|
||
- [ ] Transitive counts correct
|
||
|
||
### Phase 2: Impact Calculation (0.5-1 day)
|
||
|
||
1. Calculate impact level per port
|
||
2. Analyze property access patterns
|
||
3. Generate overall impact summary
|
||
4. Create impact reasons
|
||
|
||
**Verification:**
|
||
- [ ] Impact levels sensible
|
||
- [ ] Property analysis works
|
||
- [ ] Summary helpful
|
||
|
||
### Phase 3: UI - Usage Display (1 day)
|
||
|
||
1. Create `ImpactRadarView` component
|
||
2. Show usage locations with details
|
||
3. Display per-port impact
|
||
4. Add navigation to usages
|
||
|
||
**Verification:**
|
||
- [ ] Usages displayed clearly
|
||
- [ ] Port impacts visible
|
||
- [ ] Navigation works
|
||
|
||
### Phase 4: Checklist Generation (0.5 day)
|
||
|
||
1. Generate change checklist from analysis
|
||
2. Allow marking items complete
|
||
3. Prioritize checklist items
|
||
|
||
**Verification:**
|
||
- [ ] Checklist comprehensive
|
||
- [ ] Priorities sensible
|
||
- [ ] Can mark complete
|
||
|
||
### Phase 5: Polish & Integration (0.5-1 day)
|
||
|
||
1. Add impact badges to Components Panel
|
||
2. Context menu "Show Impact"
|
||
3. Quick impact preview on hover
|
||
4. Performance optimization
|
||
|
||
**Verification:**
|
||
- [ ] Badges show in panel
|
||
- [ ] Context menu works
|
||
- [ ] Performance acceptable
|
||
|
||
---
|
||
|
||
## Files to Create
|
||
|
||
```
|
||
packages/noodl-editor/src/editor/src/views/AnalysisPanel/
|
||
└── ImpactRadarView/
|
||
├── index.ts
|
||
├── ImpactRadarView.tsx
|
||
├── ImpactRadarView.module.scss
|
||
├── UsageSummary.tsx
|
||
├── UsageLocation.tsx
|
||
├── PortImpactList.tsx
|
||
├── ChangeChecklist.tsx
|
||
├── ImpactBadge.tsx
|
||
└── useImpactAnalysis.ts
|
||
|
||
packages/noodl-editor/src/editor/src/utils/graphAnalysis/
|
||
└── impact.ts
|
||
```
|
||
|
||
---
|
||
|
||
## Success Criteria
|
||
|
||
- [ ] Shows all places component is used
|
||
- [ ] Impact level calculation sensible
|
||
- [ ] Port-level analysis accurate
|
||
- [ ] Checklist helpful for changes
|
||
- [ ] Navigation to usages works
|
||
- [ ] Renders in < 1s
|
||
|
||
---
|
||
|
||
## Future Enhancements
|
||
|
||
- **"What if" simulation** - Preview changes without making them
|
||
- **Diff preview** - Show what would change in each usage
|
||
- **Auto-update** - Automatically update simple changes across usages
|
||
- **Impact history** - Track changes and their actual impact over time
|
||
|
||
---
|
||
|
||
## Risks & Mitigations
|
||
|
||
| Risk | Mitigation |
|
||
|------|------------|
|
||
| Too many usages to display | Pagination, grouping, filtering |
|
||
| Property analysis misses patterns | Start simple, expand based on real usage |
|
||
| Impact levels feel wrong | Make configurable, learn from feedback |
|
||
|
||
---
|
||
|
||
## Dependencies
|
||
|
||
- VIEW-000 Foundation
|
||
- VIEW-001 (optional, for visual representation)
|
||
|
||
## Blocks
|
||
|
||
- None (independent view)
|