mirror of
https://github.com/The-Low-Code-Foundation/OpenNoodl.git
synced 2026-01-12 23:32:55 +01:00
Working on the editor component tree
This commit is contained in:
@@ -0,0 +1,364 @@
|
||||
# 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
|
||||
<!-- Migration badge -->
|
||||
<div
|
||||
style="position:absolute; right:75px; top:1px; bottom:2px;"
|
||||
data-class="!hasMigrationNote:hidden"
|
||||
data-tooltip="View migration notes"
|
||||
data-click="onComponentBadgeClicked"
|
||||
>
|
||||
<div
|
||||
class="components-panel-migration-badge"
|
||||
data-class="migrationStatus:badge-{migrationStatus},isSelected:components-panel-item-selected"
|
||||
></div>
|
||||
</div>
|
||||
```
|
||||
|
||||
**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
|
||||
<!-- Migration filters (show only if project has migration notes) -->
|
||||
<div data-class="!hasMigrationNotes:hidden" class="components-panel-filters">
|
||||
<button
|
||||
data-class="migrationFilter=all:is-active"
|
||||
class="components-panel-filter-btn"
|
||||
data-click="onMigrationFilterClicked"
|
||||
data-filter="all"
|
||||
>
|
||||
All
|
||||
</button>
|
||||
<button
|
||||
data-class="migrationFilter=needs-review:is-active"
|
||||
class="components-panel-filter-btn badge-needs-review"
|
||||
data-click="onMigrationFilterClicked"
|
||||
data-filter="needs-review"
|
||||
>
|
||||
Needs Review (<span data-text="needsReviewCount">0</span>)
|
||||
</button>
|
||||
<button
|
||||
data-class="migrationFilter=ai-migrated:is-active"
|
||||
class="components-panel-filter-btn badge-ai-migrated"
|
||||
data-click="onMigrationFilterClicked"
|
||||
data-filter="ai-migrated"
|
||||
>
|
||||
AI Migrated (<span data-text="aiMigratedCount">0</span>)
|
||||
</button>
|
||||
<button
|
||||
data-class="migrationFilter=no-issues:is-active"
|
||||
class="components-panel-filter-btn"
|
||||
data-click="onMigrationFilterClicked"
|
||||
data-filter="no-issues"
|
||||
>
|
||||
No Issues
|
||||
</button>
|
||||
</div>
|
||||
```
|
||||
|
||||
**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).
|
||||
Reference in New Issue
Block a user