mirror of
https://github.com/The-Low-Code-Foundation/OpenNoodl.git
synced 2026-03-08 01:53:30 +01:00
Finished prototype local backends and expression editor
This commit is contained in:
@@ -10,6 +10,95 @@ These fundamental patterns apply across ALL Noodl development. Understanding the
|
||||
|
||||
---
|
||||
|
||||
## 📊 Property Expression Runtime Context (Jan 16, 2026)
|
||||
|
||||
### The Context Trap: Why evaluateExpression() Needs undefined, Not this.context
|
||||
|
||||
**Context**: TASK-006 Expressions Overhaul - Property expressions in the properties panel weren't evaluating. Error: "scope.get is not a function".
|
||||
|
||||
**The Problem**: The `evaluateExpression()` function in `expression-evaluator.js` expects either `undefined` (to use global Model) or a Model scope object. Passing `this.context` (the runtime node context with editorConnection, styles, etc.) caused the error because it lacks a `.get()` method.
|
||||
|
||||
**The Broken Pattern**:
|
||||
|
||||
```javascript
|
||||
// ❌ WRONG - this.context is NOT a Model scope
|
||||
Node.prototype._evaluateExpressionParameter = function (paramValue, portName) {
|
||||
const compiled = compileExpression(paramValue.expression);
|
||||
const result = evaluateExpression(compiled, this.context); // ☠️ scope.get is not a function
|
||||
};
|
||||
```
|
||||
|
||||
**The Correct Pattern**:
|
||||
|
||||
```javascript
|
||||
// ✅ RIGHT - Pass undefined to use global Model
|
||||
Node.prototype._evaluateExpressionParameter = function (paramValue, portName) {
|
||||
const compiled = compileExpression(paramValue.expression);
|
||||
const result = evaluateExpression(compiled, undefined); // ✅ Uses global Model
|
||||
};
|
||||
```
|
||||
|
||||
**Why This Works**:
|
||||
|
||||
The expression-evaluator's `createNoodlContext()` function does:
|
||||
|
||||
```javascript
|
||||
const scope = modelScope || Model; // Falls back to global Model
|
||||
const variablesModel = scope.get('--ndl--global-variables');
|
||||
```
|
||||
|
||||
When `undefined` is passed, it uses the global `Model` which has the `.get()` method. The runtime's `this.context` is a completely different object containing `editorConnection`, `styles`, etc.
|
||||
|
||||
**Reactive Expression Subscriptions**:
|
||||
|
||||
To make expressions update when Variables change, the expression-evaluator already has:
|
||||
|
||||
- `detectDependencies(expression)` - finds what Variables/Objects/Arrays are referenced
|
||||
- `subscribeToChanges(dependencies, callback)` - subscribes to Model changes
|
||||
|
||||
Wire these up in `_evaluateExpressionParameter`:
|
||||
|
||||
```javascript
|
||||
// Set up reactive subscription
|
||||
if (!this._expressionSubscriptions[portName]) {
|
||||
const dependencies = detectDependencies(paramValue.expression);
|
||||
if (dependencies.variables.length > 0) {
|
||||
this._expressionSubscriptions[portName] = subscribeToChanges(
|
||||
dependencies,
|
||||
function () {
|
||||
if (this._deleted) return;
|
||||
this.queueInput(portName, paramValue); // Re-evaluate
|
||||
}.bind(this)
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Critical Rules**:
|
||||
|
||||
1. **NEVER** pass `this.context` to `evaluateExpression()` - it's not a Model scope
|
||||
2. **ALWAYS** pass `undefined` to use the global Model for Variables/Objects/Arrays
|
||||
3. **Clean up subscriptions** in `_onNodeDeleted()` to prevent memory leaks
|
||||
4. **Track subscriptions by port name** to avoid duplicate listeners
|
||||
|
||||
**Applies To**:
|
||||
|
||||
- Property panel expression fields
|
||||
- Any runtime expression evaluation
|
||||
- Future expression support in other property types
|
||||
|
||||
**Time Saved**: This pattern prevents 1-2 hours debugging "scope.get is not a function" errors.
|
||||
|
||||
**Location**:
|
||||
|
||||
- Fixed in: `packages/noodl-runtime/src/node.js`
|
||||
- Expression evaluator: `packages/noodl-runtime/src/expression-evaluator.js`
|
||||
- Task: Phase 3 TASK-006 Expressions Overhaul
|
||||
|
||||
**Keywords**: expression, evaluateExpression, this.context, Model scope, scope.get, Variables, reactive, subscribeToChanges, detectDependencies
|
||||
|
||||
---
|
||||
|
||||
## 🔴 Editor/Runtime Window Separation (Jan 2026)
|
||||
|
||||
### The Invisible Boundary: Why Editor Methods Don't Exist in Runtime
|
||||
|
||||
511
dev-docs/reference/PANEL-UI-STYLE-GUIDE.md
Normal file
511
dev-docs/reference/PANEL-UI-STYLE-GUIDE.md
Normal file
@@ -0,0 +1,511 @@
|
||||
# Panel & Modal UI Style Guide
|
||||
|
||||
This guide documents the visual patterns used in OpenNoodl's editor panels and modals. **Always follow these patterns when creating new UI components.**
|
||||
|
||||
---
|
||||
|
||||
## Core Principles
|
||||
|
||||
### 1. Professional, Not Playful
|
||||
|
||||
- **NO emojis** in UI labels, buttons, or headers
|
||||
- **NO decorative icons** unless they serve a functional purpose
|
||||
- Clean, minimal aesthetic that respects the user's intelligence
|
||||
- Think "developer tool" not "consumer app"
|
||||
|
||||
### 2. Consistent Visual Language
|
||||
|
||||
- Use design tokens (CSS variables) for ALL colors
|
||||
- Consistent spacing using the spacing system (4px base unit)
|
||||
- Typography hierarchy using the Text component types
|
||||
- All interactive elements must have hover/active states
|
||||
|
||||
### 3. Dark Theme First
|
||||
|
||||
- Design for dark backgrounds (`--theme-color-bg-2`, `--theme-color-bg-3`)
|
||||
- Ensure sufficient contrast with light text
|
||||
- Colored elements should be muted, not neon
|
||||
|
||||
---
|
||||
|
||||
## Panel Structure
|
||||
|
||||
### Standard Panel Layout
|
||||
|
||||
```tsx
|
||||
<div className={css.Root}>
|
||||
{/* Header with title and close button */}
|
||||
<div className={css.Header}>
|
||||
<HStack hasSpacing>
|
||||
<Icon icon={IconName.Something} size={IconSize.Small} />
|
||||
<VStack>
|
||||
<Text textType={TextType.DefaultContrast}>Panel Title</Text>
|
||||
<Text textType={TextType.Shy} style={{ fontSize: '11px' }}>
|
||||
Subtitle or context
|
||||
</Text>
|
||||
</VStack>
|
||||
</HStack>
|
||||
<IconButton icon={IconName.Close} onClick={onClose} />
|
||||
</div>
|
||||
|
||||
{/* Toolbar with actions */}
|
||||
<div className={css.Toolbar}>{/* Filters, search, action buttons */}</div>
|
||||
|
||||
{/* Content area (scrollable) */}
|
||||
<div className={css.Content}>{/* Main panel content */}</div>
|
||||
|
||||
{/* Footer (optional - pagination, status) */}
|
||||
<div className={css.Footer}>{/* Page controls, counts */}</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Panel CSS Pattern
|
||||
|
||||
```scss
|
||||
.Root {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
background-color: var(--theme-color-bg-2);
|
||||
color: var(--theme-color-fg-default);
|
||||
}
|
||||
|
||||
.Header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 12px 16px;
|
||||
border-bottom: 1px solid var(--theme-color-bg-3);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.Toolbar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 8px 16px;
|
||||
background-color: var(--theme-color-bg-3);
|
||||
gap: 8px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.Content {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.Footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 8px 16px;
|
||||
border-top: 1px solid var(--theme-color-bg-3);
|
||||
background-color: var(--theme-color-bg-2);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Modal Structure
|
||||
|
||||
### Standard Modal Layout
|
||||
|
||||
```tsx
|
||||
<div className={css.Overlay}>
|
||||
<div className={css.Modal}>
|
||||
{/* Header */}
|
||||
<div className={css.Header}>
|
||||
<Text textType={TextType.Proud}>Modal Title</Text>
|
||||
<IconButton icon={IconName.Close} onClick={onClose} />
|
||||
</div>
|
||||
|
||||
{/* Body */}
|
||||
<div className={css.Body}>{/* Form fields, content */}</div>
|
||||
|
||||
{/* Footer with actions */}
|
||||
<div className={css.Footer}>
|
||||
<PrimaryButton label="Cancel" variant={PrimaryButtonVariant.Muted} onClick={onClose} />
|
||||
<PrimaryButton label="Create" variant={PrimaryButtonVariant.Cta} onClick={onSubmit} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Modal CSS Pattern
|
||||
|
||||
```scss
|
||||
.Overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.Modal {
|
||||
background-color: var(--theme-color-bg-2);
|
||||
border-radius: 8px;
|
||||
width: 480px;
|
||||
max-width: 90vw;
|
||||
max-height: 80vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
.Header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 16px 20px;
|
||||
border-bottom: 1px solid var(--theme-color-bg-3);
|
||||
}
|
||||
|
||||
.Body {
|
||||
padding: 20px;
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.Footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
padding: 16px 20px;
|
||||
border-top: 1px solid var(--theme-color-bg-3);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Form Elements
|
||||
|
||||
### Text Inputs
|
||||
|
||||
```scss
|
||||
.Input {
|
||||
width: 100%;
|
||||
padding: 8px 12px;
|
||||
border: 1px solid var(--theme-color-bg-3);
|
||||
border-radius: 4px;
|
||||
background-color: var(--theme-color-bg-1);
|
||||
color: var(--theme-color-fg-default);
|
||||
font-size: 13px;
|
||||
font-family: inherit;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
border-color: var(--theme-color-primary);
|
||||
}
|
||||
|
||||
&::placeholder {
|
||||
color: var(--theme-color-fg-default-shy);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Select Dropdowns
|
||||
|
||||
```scss
|
||||
.Select {
|
||||
padding: 8px 12px;
|
||||
border: 1px solid var(--theme-color-bg-3);
|
||||
border-radius: 4px;
|
||||
background-color: var(--theme-color-bg-1);
|
||||
color: var(--theme-color-fg-default);
|
||||
font-size: 13px;
|
||||
min-width: 120px;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
border-color: var(--theme-color-primary);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Form Groups
|
||||
|
||||
```scss
|
||||
.FormGroup {
|
||||
margin-bottom: 16px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.Label {
|
||||
display: block;
|
||||
margin-bottom: 6px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: var(--theme-color-fg-default);
|
||||
}
|
||||
|
||||
.HelpText {
|
||||
margin-top: 4px;
|
||||
font-size: 11px;
|
||||
color: var(--theme-color-fg-default-shy);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Data Tables & Grids
|
||||
|
||||
### Table Pattern
|
||||
|
||||
```scss
|
||||
.Grid {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 13px;
|
||||
|
||||
th {
|
||||
text-align: left;
|
||||
padding: 10px 12px;
|
||||
font-weight: 500;
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
color: var(--theme-color-fg-default-shy);
|
||||
background-color: var(--theme-color-bg-3);
|
||||
border-bottom: 1px solid var(--theme-color-bg-3);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 8px 12px;
|
||||
border-bottom: 1px solid var(--theme-color-bg-3);
|
||||
color: var(--theme-color-fg-default);
|
||||
}
|
||||
|
||||
tr:hover td {
|
||||
background-color: var(--theme-color-bg-3);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Type Badges
|
||||
|
||||
For showing data types, use subtle colored badges:
|
||||
|
||||
```scss
|
||||
.TypeBadge {
|
||||
display: inline-block;
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
font-size: 10px;
|
||||
font-weight: 500;
|
||||
color: white;
|
||||
}
|
||||
|
||||
// Use semantic colors, not hardcoded
|
||||
.TypeString {
|
||||
background-color: var(--theme-color-primary);
|
||||
}
|
||||
.TypeNumber {
|
||||
background-color: var(--theme-color-success);
|
||||
}
|
||||
.TypeBoolean {
|
||||
background-color: var(--theme-color-notice);
|
||||
}
|
||||
.TypeDate {
|
||||
background-color: #8b5cf6;
|
||||
} // Purple - no token available
|
||||
.TypePointer {
|
||||
background-color: var(--theme-color-danger);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Expandable Rows
|
||||
|
||||
For tree-like or expandable content:
|
||||
|
||||
```scss
|
||||
.ExpandableRow {
|
||||
border: 1px solid var(--theme-color-bg-3);
|
||||
border-radius: 6px;
|
||||
margin-bottom: 8px;
|
||||
overflow: hidden;
|
||||
background-color: var(--theme-color-bg-2);
|
||||
|
||||
&[data-expanded='true'] {
|
||||
border-color: var(--theme-color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.RowHeader {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 12px 16px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.15s;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--theme-color-bg-3);
|
||||
}
|
||||
}
|
||||
|
||||
.RowContent {
|
||||
padding: 0 16px 16px;
|
||||
background-color: var(--theme-color-bg-1);
|
||||
border-top: 1px solid var(--theme-color-bg-3);
|
||||
}
|
||||
|
||||
.ExpandIcon {
|
||||
transition: transform 0.2s;
|
||||
color: var(--theme-color-fg-default-shy);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Empty States
|
||||
|
||||
When there's no content to show:
|
||||
|
||||
```scss
|
||||
.EmptyState {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 48px 24px;
|
||||
text-align: center;
|
||||
color: var(--theme-color-fg-default-shy);
|
||||
|
||||
.EmptyIcon {
|
||||
font-size: 48px;
|
||||
margin-bottom: 16px;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.EmptyText {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Loading & Error States
|
||||
|
||||
### Loading
|
||||
|
||||
```scss
|
||||
.Loading {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 48px;
|
||||
color: var(--theme-color-fg-default-shy);
|
||||
}
|
||||
```
|
||||
|
||||
### Error
|
||||
|
||||
```scss
|
||||
.Error {
|
||||
padding: 12px 16px;
|
||||
background-color: rgba(239, 68, 68, 0.1);
|
||||
border: 1px solid var(--theme-color-danger);
|
||||
border-radius: 4px;
|
||||
color: var(--theme-color-danger);
|
||||
font-size: 13px;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Button Patterns
|
||||
|
||||
### Use PrimaryButton Variants Correctly
|
||||
|
||||
| Variant | Use For |
|
||||
| -------- | ------------------------------------------ |
|
||||
| `Cta` | Primary action (Create, Save, Submit) |
|
||||
| `Muted` | Secondary action (Cancel, Close, Refresh) |
|
||||
| `Ghost` | Tertiary action (Edit, View, minor action) |
|
||||
| `Danger` | Destructive action (Delete) |
|
||||
|
||||
### Button Sizing
|
||||
|
||||
- `Small` - In toolbars, table rows, compact spaces
|
||||
- `Medium` - Modal footers, standalone actions
|
||||
- `Large` - Rarely used, hero actions only
|
||||
|
||||
---
|
||||
|
||||
## Spacing System
|
||||
|
||||
Use consistent spacing based on 4px unit:
|
||||
|
||||
| Token | Value | Use For |
|
||||
| ----- | ----- | ------------------------ |
|
||||
| `xs` | 4px | Tight spacing, icon gaps |
|
||||
| `sm` | 8px | Related elements |
|
||||
| `md` | 12px | Standard padding |
|
||||
| `lg` | 16px | Section padding |
|
||||
| `xl` | 24px | Large gaps |
|
||||
| `xxl` | 32px | Major sections |
|
||||
|
||||
---
|
||||
|
||||
## Typography
|
||||
|
||||
### Use Text Component Types
|
||||
|
||||
| Type | Use For |
|
||||
| ----------------- | ------------------------------- |
|
||||
| `Proud` | Panel titles, modal headers |
|
||||
| `DefaultContrast` | Primary content, item names |
|
||||
| `Default` | Body text, descriptions |
|
||||
| `Shy` | Secondary text, hints, metadata |
|
||||
|
||||
### Font Sizes
|
||||
|
||||
- Headers: 14-16px
|
||||
- Body: 13px
|
||||
- Labels: 12px
|
||||
- Small text: 11px
|
||||
- Badges: 10px
|
||||
|
||||
---
|
||||
|
||||
## Don'ts
|
||||
|
||||
❌ **Don't use emojis** in buttons or labels
|
||||
❌ **Don't use hardcoded colors** - always use CSS variables
|
||||
❌ **Don't use bright/neon colors** - keep it muted
|
||||
❌ **Don't use decorative icons** that don't convey meaning
|
||||
❌ **Don't use rounded corners > 8px** - keep it subtle
|
||||
❌ **Don't use shadows > 0.4 opacity** - stay subtle
|
||||
❌ **Don't use animation duration > 200ms** - keep it snappy
|
||||
❌ **Don't mix different styling approaches** - be consistent
|
||||
|
||||
---
|
||||
|
||||
## Reference Components
|
||||
|
||||
For working examples, see:
|
||||
|
||||
- `packages/noodl-editor/src/editor/src/views/panels/schemamanager/`
|
||||
- `packages/noodl-editor/src/editor/src/views/panels/databrowser/`
|
||||
- `packages/noodl-editor/src/editor/src/views/panels/BackendServicesPanel/`
|
||||
|
||||
---
|
||||
|
||||
_Last Updated: January 2026_
|
||||
@@ -0,0 +1,219 @@
|
||||
# Phase 2B: Inline Property Expressions - COMPLETE ✅
|
||||
|
||||
**Started:** 2026-01-10
|
||||
**Completed:** 2026-01-16
|
||||
**Status:** ✅ COMPLETE
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
Inline property expressions are now fully functional! Users can set property values using JavaScript expressions that reference Noodl.Variables, Noodl.Objects, and Noodl.Arrays. Expressions update reactively when their dependencies change.
|
||||
|
||||
---
|
||||
|
||||
## ✅ What Was Implemented
|
||||
|
||||
### 1. ExpressionInput Component - COMPLETE ✅
|
||||
|
||||
**Files Created:**
|
||||
|
||||
- `packages/noodl-core-ui/src/components/property-panel/ExpressionInput/ExpressionInput.tsx`
|
||||
- `packages/noodl-core-ui/src/components/property-panel/ExpressionInput/ExpressionInput.module.scss`
|
||||
|
||||
**Features:**
|
||||
|
||||
- Monospace input field with "fx" badge
|
||||
- Expression mode toggle
|
||||
- Expand button to open full editor modal
|
||||
- Error state display
|
||||
|
||||
---
|
||||
|
||||
### 2. ExpressionEditorModal - COMPLETE ✅
|
||||
|
||||
**Files Created:**
|
||||
|
||||
- `packages/noodl-editor/src/editor/src/views/panels/propertyeditor/ExpressionEditorModal/ExpressionEditorModal.tsx`
|
||||
- `packages/noodl-editor/src/editor/src/views/panels/propertyeditor/ExpressionEditorModal/ExpressionEditorModal.module.scss`
|
||||
- `packages/noodl-editor/src/editor/src/views/panels/propertyeditor/ExpressionEditorModal/index.ts`
|
||||
|
||||
**Features:**
|
||||
|
||||
- Full-screen modal for editing complex expressions
|
||||
- Uses JavaScriptEditor (CodeMirror) for syntax highlighting
|
||||
- Help documentation showing available globals (Noodl.Variables, etc.)
|
||||
- Apply/Cancel buttons
|
||||
|
||||
---
|
||||
|
||||
### 3. PropertyPanelInput Integration - COMPLETE ✅
|
||||
|
||||
**Files Modified:**
|
||||
|
||||
- `packages/noodl-core-ui/src/components/property-panel/PropertyPanelInput/PropertyPanelInput.tsx`
|
||||
|
||||
**Features:**
|
||||
|
||||
- Added expression-related props (supportsExpression, expressionMode, expression, etc.)
|
||||
- Conditional rendering: expression input vs fixed input
|
||||
- Mode change handlers
|
||||
|
||||
---
|
||||
|
||||
### 4. PropertyPanelInputWithExpressionModal Wrapper - COMPLETE ✅
|
||||
|
||||
**Files Created:**
|
||||
|
||||
- `packages/noodl-editor/src/editor/src/views/panels/propertyeditor/components/PropertyPanelInputWithExpressionModal.tsx`
|
||||
|
||||
**Features:**
|
||||
|
||||
- Combines PropertyPanelInput with ExpressionEditorModal
|
||||
- Manages modal open/close state
|
||||
- Handles expression expand functionality
|
||||
|
||||
---
|
||||
|
||||
### 5. BasicType Property Editor Wiring - COMPLETE ✅
|
||||
|
||||
**Files Modified:**
|
||||
|
||||
- `packages/noodl-editor/src/editor/src/views/panels/propertyeditor/DataTypes/BasicType.ts`
|
||||
|
||||
**Features:**
|
||||
|
||||
- Uses React for rendering via createRoot
|
||||
- Supports expression mode toggle
|
||||
- Integrates with ExpressionParameter model
|
||||
- Uses ParameterValueResolver for safe value display
|
||||
|
||||
---
|
||||
|
||||
### 6. Runtime Reactive Subscriptions - COMPLETE ✅
|
||||
|
||||
**Files Modified:**
|
||||
|
||||
- `packages/noodl-runtime/src/node.js`
|
||||
|
||||
**Features:**
|
||||
|
||||
- `_evaluateExpressionParameter()` now sets up reactive subscriptions
|
||||
- Uses `detectDependencies()` to find referenced Variables/Objects/Arrays
|
||||
- Uses `subscribeToChanges()` to listen for dependency updates
|
||||
- Re-queues input when dependencies change
|
||||
- Cleanup in `_onNodeDeleted()`
|
||||
|
||||
---
|
||||
|
||||
## 🎯 How It Works
|
||||
|
||||
### User Experience
|
||||
|
||||
1. Select a node with text/number properties
|
||||
2. Click the "fx" button to toggle expression mode
|
||||
3. Enter an expression like `Noodl.Variables.userName`
|
||||
4. The value updates automatically when the variable changes
|
||||
5. Click the expand button for a full code editor modal
|
||||
|
||||
### Supported Expressions
|
||||
|
||||
```javascript
|
||||
// Simple variable access
|
||||
Noodl.Variables.userName;
|
||||
|
||||
// Math operations
|
||||
Noodl.Variables.count * 2 + 1;
|
||||
|
||||
// Ternary conditionals
|
||||
Noodl.Variables.isLoggedIn ? 'Logout' : 'Login';
|
||||
|
||||
// String concatenation
|
||||
'Hello, ' + Noodl.Variables.userName + '!';
|
||||
|
||||
// Math helpers
|
||||
min(Noodl.Variables.price, 100);
|
||||
max(10, Noodl.Variables.quantity);
|
||||
round(Noodl.Variables.total);
|
||||
|
||||
// Object/Array access
|
||||
Noodl.Objects['user'].name;
|
||||
Noodl.Arrays['items'].length;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📁 Files Changed
|
||||
|
||||
### Created
|
||||
|
||||
- `packages/noodl-core-ui/src/components/property-panel/ExpressionInput/ExpressionInput.tsx`
|
||||
- `packages/noodl-core-ui/src/components/property-panel/ExpressionInput/ExpressionInput.module.scss`
|
||||
- `packages/noodl-editor/src/editor/src/views/panels/propertyeditor/ExpressionEditorModal/ExpressionEditorModal.tsx`
|
||||
- `packages/noodl-editor/src/editor/src/views/panels/propertyeditor/ExpressionEditorModal/ExpressionEditorModal.module.scss`
|
||||
- `packages/noodl-editor/src/editor/src/views/panels/propertyeditor/ExpressionEditorModal/index.ts`
|
||||
- `packages/noodl-editor/src/editor/src/views/panels/propertyeditor/components/PropertyPanelInputWithExpressionModal.tsx`
|
||||
|
||||
### Modified
|
||||
|
||||
- `packages/noodl-core-ui/src/components/property-panel/PropertyPanelInput/PropertyPanelInput.tsx`
|
||||
- `packages/noodl-editor/src/editor/src/views/panels/propertyeditor/DataTypes/BasicType.ts`
|
||||
- `packages/noodl-runtime/src/node.js` (added reactive subscriptions)
|
||||
|
||||
---
|
||||
|
||||
## 🐛 Bug Fixes (2026-01-16)
|
||||
|
||||
### Modal Showing Stale Expression
|
||||
|
||||
**Problem:** When editing an expression in the inline input field and then opening the modal, the modal showed the old expression text instead of the current one.
|
||||
|
||||
**Root Cause:** `BasicType.ts` was not re-rendering after `onExpressionChange`, so the modal component never received the updated expression prop.
|
||||
|
||||
**Fix:** Added `setTimeout(() => this.renderReact(), 0)` at the end of `onExpressionChange` in `BasicType.ts` to ensure the React tree updates with the new expression value.
|
||||
|
||||
### Variable Changes Not Updating Node Values
|
||||
|
||||
**Problem:** When using an expression like `Noodl.Variables.label_text`, changing the variable value didn't update the node's property until the expression was manually saved again.
|
||||
|
||||
**Root Cause:** The subscription callback in `node.js` captured the old `paramValue` object in a closure. When the subscription fired, it re-queued the old expression, not the current one stored in `_inputValues`.
|
||||
|
||||
**Fix:** Updated subscription management in `_evaluateExpressionParameter()`:
|
||||
|
||||
- Subscriptions now track both the unsubscribe function AND the expression string
|
||||
- When expression changes, old subscription is unsubscribed before creating a new one
|
||||
- The callback now reads from `this._inputValues[portName]` (current value) instead of using a closure
|
||||
|
||||
**Files Modified:**
|
||||
|
||||
- `packages/noodl-editor/src/editor/src/views/panels/propertyeditor/DataTypes/BasicType.ts`
|
||||
- `packages/noodl-runtime/src/node.js`
|
||||
|
||||
---
|
||||
|
||||
## 🎓 Key Learnings
|
||||
|
||||
### Context Issue
|
||||
|
||||
The `evaluateExpression()` function in expression-evaluator.js expects either `undefined` or a Model scope as the second parameter. Passing `this.context` (the runtime context with editorConnection, styles, etc.) caused "scope.get is not a function" errors. **Solution:** Pass `undefined` to use the global Model.
|
||||
|
||||
### Reactive Updates
|
||||
|
||||
The expression-evaluator module already had `detectDependencies()` and `subscribeToChanges()` functions. We just needed to wire them into `_evaluateExpressionParameter()` in node.js to enable reactive updates.
|
||||
|
||||
### Value Display
|
||||
|
||||
Expression parameters are objects (`{ mode: 'expression', expression: '...', fallback: ... }`). When displaying the value in the properties panel, we need to extract the fallback value, not display the object. The `ParameterValueResolver.toString()` helper handles this.
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Future Enhancements
|
||||
|
||||
1. **Canvas rendering** - Show expression indicator on node ports (TASK-006B)
|
||||
2. **More property types** - Extend to color, enum, and other types
|
||||
3. **Expression autocomplete** - IntelliSense for Noodl.Variables names
|
||||
4. **Expression validation** - Real-time syntax checking
|
||||
|
||||
---
|
||||
|
||||
**Last Updated:** 2026-01-16
|
||||
@@ -0,0 +1,107 @@
|
||||
# TASK-007H: Schema Manager UI - COMPLETE
|
||||
|
||||
## Summary
|
||||
|
||||
Implemented a Schema Manager UI panel for viewing and managing database schemas in local SQLite backends.
|
||||
|
||||
## Files Created
|
||||
|
||||
### Schema Manager Panel Components
|
||||
|
||||
- `packages/noodl-editor/src/editor/src/views/panels/schemamanager/SchemaPanel.tsx` - Main panel showing tables list
|
||||
- `packages/noodl-editor/src/editor/src/views/panels/schemamanager/SchemaPanel.module.scss` - Panel styles
|
||||
- `packages/noodl-editor/src/editor/src/views/panels/schemamanager/TableRow.tsx` - Expandable table row with columns
|
||||
- `packages/noodl-editor/src/editor/src/views/panels/schemamanager/TableRow.module.scss` - Table row styles
|
||||
- `packages/noodl-editor/src/editor/src/views/panels/schemamanager/index.ts` - Module exports
|
||||
|
||||
## Files Modified
|
||||
|
||||
### BackendManager.js
|
||||
|
||||
Added IPC handlers for schema operations:
|
||||
|
||||
- `backend:getSchema` - Get full schema for a backend
|
||||
- `backend:getTableSchema` - Get single table schema
|
||||
- `backend:getRecordCount` - Get record count for a table
|
||||
- `backend:createTable` - Create a new table
|
||||
- `backend:addColumn` - Add column to existing table
|
||||
|
||||
### LocalBackendCard.tsx
|
||||
|
||||
- Added "Schema" button (shows when backend is running)
|
||||
- Added schema panel overlay state
|
||||
- Integrated SchemaPanel component
|
||||
|
||||
### LocalBackendCard.module.scss
|
||||
|
||||
- Added `.SchemaPanelOverlay` styles for full-screen modal:
|
||||
- Uses React Portal (`createPortal`) to render at `document.body`
|
||||
- `z-index: 9999` ensures overlay above all UI elements
|
||||
- Dark backdrop (`rgba(0, 0, 0, 0.85)`)
|
||||
- Centered modal with max-width 900px
|
||||
- Proper border-radius, shadow, and background styling
|
||||
|
||||
## Features Implemented
|
||||
|
||||
### SchemaPanel
|
||||
|
||||
- Shows list of all tables in the backend
|
||||
- Displays table count in header
|
||||
- Refresh button to reload schema
|
||||
- Empty state with "Create First Table" prompt (placeholder)
|
||||
- Close button to dismiss panel
|
||||
|
||||
### TableRow
|
||||
|
||||
- Collapsible rows with expand/collapse toggle
|
||||
- Table icon with "T" badge
|
||||
- Shows field count and record count
|
||||
- Expanded view shows:
|
||||
- System columns (objectId, createdAt, updatedAt)
|
||||
- User-defined columns with type badges
|
||||
- Required indicator
|
||||
- Default value display
|
||||
- Type badges with color-coded data types:
|
||||
- String (primary)
|
||||
- Number (success/green)
|
||||
- Boolean (notice/yellow)
|
||||
- Date (purple)
|
||||
- Object (pink)
|
||||
- Array (indigo)
|
||||
- Pointer/Relation (red)
|
||||
- GeoPoint (teal)
|
||||
- File (orange)
|
||||
|
||||
## Usage
|
||||
|
||||
1. Create and start a local backend
|
||||
2. Click "Schema" button on the backend card
|
||||
3. View tables and expand rows to see columns
|
||||
4. Click "Refresh" to reload schema
|
||||
5. Click "Close" to dismiss panel
|
||||
|
||||
## Future Enhancements (Deferred)
|
||||
|
||||
The following were scoped but not implemented in this initial version:
|
||||
|
||||
- CreateTableModal - For creating new tables
|
||||
- ColumnEditor - For editing column definitions
|
||||
- SchemaEditor - Full table editor
|
||||
- ExportDialog - Export schema to SQL formats (handlers exist in BackendManager)
|
||||
|
||||
These can be added incrementally as needed.
|
||||
|
||||
## Verification
|
||||
|
||||
The implementation follows project patterns:
|
||||
|
||||
- ✅ Uses theme tokens (no hardcoded colors)
|
||||
- ✅ Uses IPC pattern from useLocalBackends
|
||||
- ✅ Proper TypeScript types
|
||||
- ✅ JSDoc documentation
|
||||
- ✅ Module SCSS with CSS modules
|
||||
- ✅ Follows existing component structure
|
||||
|
||||
---
|
||||
|
||||
Completed: 15/01/2026
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,205 @@
|
||||
# TASK-007I: Data Browser Panel - COMPLETE
|
||||
|
||||
## Summary
|
||||
|
||||
Implemented a full-featured data browser panel for viewing and editing records in local backend SQLite tables, providing a spreadsheet-like interface with inline editing, search, pagination, and CRUD operations.
|
||||
|
||||
## Status: ✅ Complete
|
||||
|
||||
## Date: 2026-01-15
|
||||
|
||||
---
|
||||
|
||||
## What Was Implemented
|
||||
|
||||
### 1. Backend IPC Handlers (BackendManager.js)
|
||||
|
||||
Added four new IPC handlers for data operations:
|
||||
|
||||
- **`backend:queryRecords`** - Query records with pagination, sorting, search filters
|
||||
- **`backend:createRecord`** - Create a new record in a table
|
||||
- **`backend:saveRecord`** - Update an existing record (inline edit)
|
||||
- **`backend:deleteRecord`** - Delete a single record
|
||||
|
||||
These handlers wrap the existing LocalSQLAdapter CRUD methods (query, create, save, delete) and expose them to the renderer process.
|
||||
|
||||
### 2. DataBrowser Component (`panels/databrowser/`)
|
||||
|
||||
Created a complete data browser panel with the following components:
|
||||
|
||||
#### DataBrowser.tsx (Main Component)
|
||||
|
||||
- Table selector dropdown
|
||||
- Search input with cross-field text search
|
||||
- Pagination with page navigation (50 records per page)
|
||||
- Bulk selection and delete operations
|
||||
- CSV export functionality
|
||||
- Loading states and error handling
|
||||
- New record modal trigger
|
||||
|
||||
#### DataGrid.tsx (Spreadsheet View)
|
||||
|
||||
- Column headers with type badges (String, Number, Boolean, Date, Object, Array)
|
||||
- System columns (objectId, createdAt, updatedAt) displayed as read-only
|
||||
- Inline editing - click any editable cell to edit
|
||||
- Row selection with checkbox
|
||||
- Select all/none toggle
|
||||
- Delete action per row
|
||||
- Scrollable with sticky header
|
||||
|
||||
#### CellEditor.tsx (Inline Editor)
|
||||
|
||||
- Type-aware input controls:
|
||||
- **String**: Text input
|
||||
- **Number**: Number input with step="any"
|
||||
- **Boolean**: Checkbox with immediate save
|
||||
- **Date**: datetime-local input
|
||||
- **Object/Array**: Multi-line JSON editor with Save/Cancel buttons
|
||||
- Auto-focus and select on mount
|
||||
- Enter to save, Escape to cancel (for simple types)
|
||||
- Validation for numbers and JSON parsing
|
||||
- Error display for invalid input
|
||||
|
||||
#### NewRecordModal.tsx (Create Record Form)
|
||||
|
||||
- Form fields generated from column schema
|
||||
- Type-aware input controls for each field type
|
||||
- Required field validation
|
||||
- Default values based on column defaults or type defaults
|
||||
- Success callback to refresh data grid
|
||||
|
||||
### 3. Styling
|
||||
|
||||
All components use theme CSS variables per UI-STYLING-GUIDE.md:
|
||||
|
||||
- `--theme-color-bg-*` for backgrounds
|
||||
- `--theme-color-fg-*` for text
|
||||
- `--theme-color-border-*` for borders
|
||||
- `--theme-color-primary*` for accents
|
||||
- `--theme-color-success/danger/notice` for status colors
|
||||
|
||||
### 4. Integration
|
||||
|
||||
Added "Data" button to LocalBackendCard (visible when backend is running):
|
||||
|
||||
- Opens DataBrowser in a full-screen portal overlay
|
||||
- Same pattern as Schema panel
|
||||
|
||||
---
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
### Created Files:
|
||||
|
||||
```
|
||||
packages/noodl-editor/src/editor/src/views/panels/databrowser/
|
||||
├── DataBrowser.tsx - Main data browser component
|
||||
├── DataBrowser.module.scss - Styles for DataBrowser
|
||||
├── DataGrid.tsx - Spreadsheet grid component
|
||||
├── DataGrid.module.scss - Styles for DataGrid
|
||||
├── CellEditor.tsx - Inline cell editor
|
||||
├── CellEditor.module.scss - Styles for CellEditor
|
||||
├── NewRecordModal.tsx - Create record modal
|
||||
├── NewRecordModal.module.scss - Styles for modal
|
||||
└── index.ts - Exports
|
||||
```
|
||||
|
||||
### Modified Files:
|
||||
|
||||
```
|
||||
packages/noodl-editor/src/main/src/local-backend/BackendManager.js
|
||||
- Added queryRecords(), createRecord(), saveRecord(), deleteRecord() methods
|
||||
- Added corresponding IPC handlers
|
||||
|
||||
packages/noodl-editor/src/editor/src/views/panels/BackendServicesPanel/LocalBackendCard/LocalBackendCard.tsx
|
||||
- Added DataBrowser import
|
||||
- Added showDataBrowser state
|
||||
- Added "Data" button
|
||||
- Added DataBrowser portal rendering
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## How To Use
|
||||
|
||||
1. Start a local backend from the Backend Services panel
|
||||
2. Click the "Data" button on the running backend card
|
||||
3. Select a table from the dropdown (auto-selects first table if available)
|
||||
4. Browse records with pagination
|
||||
5. Click any editable cell to edit inline
|
||||
6. Use "+ New Record" to add records
|
||||
7. Use checkboxes for bulk selection and delete
|
||||
8. Use "Export CSV" to download current view
|
||||
|
||||
---
|
||||
|
||||
## Technical Notes
|
||||
|
||||
### Query API
|
||||
|
||||
The query API supports:
|
||||
|
||||
```javascript
|
||||
{
|
||||
collection: string, // Table name
|
||||
limit: number, // Max records (default 50)
|
||||
skip: number, // Pagination offset
|
||||
where: object, // Filter conditions ($or, contains, etc.)
|
||||
sort: string[], // Sort order (e.g., ['-createdAt'])
|
||||
count: boolean // Return total count for pagination
|
||||
}
|
||||
```
|
||||
|
||||
### Search Implementation
|
||||
|
||||
Search uses `$or` with `contains` operator across all String columns and objectId. The LocalSQLAdapter converts this to SQLite LIKE queries.
|
||||
|
||||
### Type Handling
|
||||
|
||||
- System fields (objectId, createdAt, updatedAt) are always read-only
|
||||
- Boolean cells save immediately on checkbox toggle
|
||||
- Object/Array cells require explicit Save button due to JSON editing
|
||||
- Dates are stored as ISO8601 strings
|
||||
|
||||
---
|
||||
|
||||
## Future Improvements (Not in scope)
|
||||
|
||||
- Column sorting by clicking headers
|
||||
- Filter by specific field values
|
||||
- Relation/Pointer field expansion
|
||||
- Inline record duplication
|
||||
- Undo for delete operations
|
||||
- Import CSV/JSON
|
||||
|
||||
---
|
||||
|
||||
## Verification Checklist
|
||||
|
||||
- [x] IPC handlers added to BackendManager.js
|
||||
- [x] DataBrowser component created
|
||||
- [x] DataGrid with inline editing
|
||||
- [x] CellEditor with type-aware controls
|
||||
- [x] NewRecordModal for creating records
|
||||
- [x] CSV export functionality
|
||||
- [x] Pagination working
|
||||
- [x] Search functionality
|
||||
- [x] Bulk delete operations
|
||||
- [x] Integration with LocalBackendCard
|
||||
- [x] Theme-compliant styling
|
||||
- [x] No hardcoded colors
|
||||
- [x] JSDoc comments added
|
||||
|
||||
---
|
||||
|
||||
## Related Tasks
|
||||
|
||||
- **TASK-007A**: Core SQLite adapter ✅
|
||||
- **TASK-007B**: Workflow integration ✅
|
||||
- **TASK-007C**: Schema management ✅
|
||||
- **TASK-007D**: Backend lifecycle ✅
|
||||
- **TASK-007E**: Express HTTP API ✅
|
||||
- **TASK-007F**: Node integration ✅
|
||||
- **TASK-007G**: UI components ✅
|
||||
- **TASK-007H**: Schema Panel UI ✅
|
||||
- **TASK-007I**: Data Browser ✅ (this task)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,90 @@
|
||||
# TASK-007J: Schema/Database Management UX - COMPLETE
|
||||
|
||||
## Status: ✅ COMPLETE (with Known Issues)
|
||||
|
||||
## Summary
|
||||
|
||||
Built Schema Manager and Data Browser UI panels with basic CRUD functionality. Uses `id` (UUID v4) as the primary key field to be compatible with Noodl frontend objects/arrays.
|
||||
|
||||
## What Was Built
|
||||
|
||||
### Schema Manager Panel
|
||||
|
||||
- **SchemaPanel** - Main panel for viewing/managing database schemas
|
||||
- **TableRow** - Expandable row showing table name, columns, record count
|
||||
- **CreateTableModal** - Modal for creating new tables with initial columns
|
||||
- **AddColumnForm** - Form for adding columns (type dropdown, required checkbox)
|
||||
|
||||
### Data Browser Panel
|
||||
|
||||
- **DataBrowser** - Main data browsing UI with table selector, search, pagination
|
||||
- **DataGrid** - Spreadsheet-style grid with inline editing
|
||||
- **CellEditor** - Inline cell editor with type-aware input handling
|
||||
- **NewRecordModal** - Modal for creating new records
|
||||
|
||||
### Backend Changes
|
||||
|
||||
- Changed primary key from `objectId` to `id` with UUID v4
|
||||
- QueryBuilder uses `id` field for all operations
|
||||
- LocalSQLAdapter generates RFC 4122 UUIDs
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
### New UI Components
|
||||
|
||||
- `packages/noodl-editor/src/editor/src/views/panels/schemamanager/`
|
||||
|
||||
- `SchemaPanel.tsx`, `SchemaPanel.module.scss`
|
||||
- `TableRow.tsx`, `TableRow.module.scss`
|
||||
- `CreateTableModal.tsx`, `CreateTableModal.module.scss`
|
||||
- `AddColumnForm.tsx`, `AddColumnForm.module.scss`
|
||||
- `index.ts`
|
||||
|
||||
- `packages/noodl-editor/src/editor/src/views/panels/databrowser/`
|
||||
- `DataBrowser.tsx`, `DataBrowser.module.scss`
|
||||
- `DataGrid.tsx`, `DataGrid.module.scss`
|
||||
- `CellEditor.tsx`, `CellEditor.module.scss`
|
||||
- `NewRecordModal.tsx`, `NewRecordModal.module.scss`
|
||||
- `index.ts`
|
||||
|
||||
### Modified Backend Files
|
||||
|
||||
- `packages/noodl-runtime/src/api/adapters/local-sql/QueryBuilder.js`
|
||||
- `packages/noodl-runtime/src/api/adapters/local-sql/LocalSQLAdapter.js`
|
||||
|
||||
## Known Issues (Future Task)
|
||||
|
||||
The following bugs need to be addressed in a follow-up task:
|
||||
|
||||
### Data Browser Bugs
|
||||
|
||||
1. **Object/Array fields don't work** - Can't type into object/array field editors
|
||||
2. **Cell editor focus issues** - Sometimes loses focus unexpectedly
|
||||
3. **Search functionality limited** - Only searches String fields
|
||||
|
||||
### Schema Manager Bugs
|
||||
|
||||
1. **Can't edit existing tables** - Edit button only expands row, no add/remove columns
|
||||
2. **Can't delete tables** - No delete table functionality
|
||||
|
||||
### SQLite/Backend Bugs
|
||||
|
||||
1. **Real SQLite database doesn't work** - Falls back to in-memory mock
|
||||
2. **better-sqlite3 import issues** - Electron compatibility problems
|
||||
3. **Schema persistence** - Schema changes don't persist properly
|
||||
4. **Query filtering** - Complex WHERE clauses may not work correctly
|
||||
|
||||
### UI/UX Issues
|
||||
|
||||
1. **Boolean toggle needs improvement** - Checkbox isn't very intuitive
|
||||
2. **Date picker needed** - Currently just text input for dates
|
||||
3. **Pointer/Relation fields** - No UI for selecting related records
|
||||
4. **File upload** - No file upload/browse functionality
|
||||
|
||||
## Next Steps
|
||||
|
||||
See **TASK-007K-DRAFT.md** for bug fixes and improvements.
|
||||
|
||||
## Completion Date
|
||||
|
||||
January 15, 2026
|
||||
@@ -0,0 +1,260 @@
|
||||
# TASK-007J: Schema & Data Creation UX
|
||||
|
||||
## Status: 📋 Draft
|
||||
|
||||
## Summary
|
||||
|
||||
Add the missing creation UX to the Schema and Data Browser panels - create tables, add/edit/delete columns, and create data records with proper type handling.
|
||||
|
||||
---
|
||||
|
||||
## Problem
|
||||
|
||||
The current implementation (007H + 007I) provides excellent **viewing** and **inline editing** capabilities, but is missing the ability to **create new structures from scratch**:
|
||||
|
||||
- Schema Panel shows tables but no way to create a new table
|
||||
- No way to add/edit/delete columns in existing tables
|
||||
- Data Browser has "+ New Record" but useless without tables
|
||||
- Field types need proper UI for creation (default values, constraints)
|
||||
|
||||
---
|
||||
|
||||
## Dependencies
|
||||
|
||||
**Backend (Already Exists ✅):**
|
||||
|
||||
- `backend:createTable` - IPC handler in BackendManager.js
|
||||
- `backend:addColumn` - IPC handler in BackendManager.js
|
||||
- LocalSQLAdapter has all CRUD methods for schema changes
|
||||
|
||||
**UI Components (Already Exists ✅):**
|
||||
|
||||
- SchemaPanel - just needs buttons and modals wired up
|
||||
- DataBrowser - just needs better empty state
|
||||
|
||||
---
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
### Part 1: Create Table Modal
|
||||
|
||||
**New Component: `CreateTableModal.tsx`**
|
||||
|
||||
Features:
|
||||
|
||||
- Table name input (validated: alphanumeric, no spaces, unique)
|
||||
- Initial columns list with ability to add/remove
|
||||
- For each column:
|
||||
- Name input
|
||||
- Type dropdown (String, Number, Boolean, Date, Object, Array)
|
||||
- Required checkbox
|
||||
- Default value input (type-aware)
|
||||
- "Create Table" button calls `backend:createTable`
|
||||
|
||||
**Supported Field Types:**
|
||||
| Type | SQLite | Default Input |
|
||||
|------|--------|---------------|
|
||||
| String | TEXT | Text input |
|
||||
| Number | REAL | Number input |
|
||||
| Boolean | INTEGER | Checkbox |
|
||||
| Date | TEXT (ISO) | Date picker |
|
||||
| Object | TEXT (JSON) | JSON editor |
|
||||
| Array | TEXT (JSON) | JSON editor |
|
||||
|
||||
**Integration:**
|
||||
|
||||
- Add `showCreateTableModal` state to SchemaPanel
|
||||
- Wire up "+ New Table" button (already in header)
|
||||
- Call `loadSchema()` on success
|
||||
|
||||
### Part 2: Add Column to Existing Table
|
||||
|
||||
**New Component: `AddColumnForm.tsx`**
|
||||
|
||||
Features:
|
||||
|
||||
- Inline form that appears in expanded table view
|
||||
- Column name input
|
||||
- Type dropdown
|
||||
- Required checkbox
|
||||
- Default value (required for non-nullable columns if table has data)
|
||||
- "Add Column" button calls `backend:addColumn`
|
||||
|
||||
**Integration:**
|
||||
|
||||
- Add to TableRow component's expanded view
|
||||
- Add "+ Add Column" button to expanded section
|
||||
- Handles the case where existing records need default values
|
||||
|
||||
### Part 3: Edit Column (Rename/Change Type)
|
||||
|
||||
**Note:** SQLite has limited ALTER TABLE support. Only RENAME COLUMN is safe.
|
||||
|
||||
**Features:**
|
||||
|
||||
- Edit icon next to each column in expanded view
|
||||
- Opens inline editor for column name
|
||||
- Type change is **not supported** (would need table recreation)
|
||||
- Shows tooltip: "Type cannot be changed after creation"
|
||||
|
||||
### Part 4: Delete Column
|
||||
|
||||
**New IPC Handler Needed:** `backend:deleteColumn`
|
||||
|
||||
**Note:** SQLite doesn't support DROP COLUMN directly - needs table recreation.
|
||||
|
||||
**Implementation options:**
|
||||
|
||||
1. **Not supported** - show tooltip "SQLite doesn't support column deletion"
|
||||
2. **Recreate table** - expensive but possible:
|
||||
- Create new table without column
|
||||
- Copy data
|
||||
- Drop old table
|
||||
- Rename new table
|
||||
|
||||
**Recommendation:** Option 1 for MVP, Option 2 as enhancement
|
||||
|
||||
### Part 5: Delete Table
|
||||
|
||||
**New IPC Handler Needed:** `backend:deleteTable` (or use existing drop)
|
||||
|
||||
**Features:**
|
||||
|
||||
- Delete/trash icon on each table row
|
||||
- Confirmation dialog: "Delete table {name}? This will permanently delete all data."
|
||||
- Calls `backend:deleteTable`
|
||||
|
||||
### Part 6: Improved Empty States
|
||||
|
||||
**Schema Panel (when no tables):**
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────┐
|
||||
│ 🗄️ No tables yet │
|
||||
│ │
|
||||
│ Create your first table to start │
|
||||
│ storing data in your backend. │
|
||||
│ │
|
||||
│ [+ Create Table] │
|
||||
└─────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Data Browser (when no tables):**
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────┐
|
||||
│ 📊 No tables in database │
|
||||
│ │
|
||||
│ Go to Schema panel to create │
|
||||
│ your first table, then return │
|
||||
│ here to browse and edit data. │
|
||||
│ │
|
||||
│ [Open Schema Panel] │
|
||||
└─────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Files to Create/Modify
|
||||
|
||||
### New Files:
|
||||
|
||||
```
|
||||
panels/schemamanager/
|
||||
├── CreateTableModal.tsx - Modal for creating new tables
|
||||
├── CreateTableModal.module.scss
|
||||
├── AddColumnForm.tsx - Inline form for adding columns
|
||||
├── AddColumnForm.module.scss
|
||||
├── ColumnEditor.tsx - Inline column name editor
|
||||
└── ColumnEditor.module.scss
|
||||
```
|
||||
|
||||
### Modified Files:
|
||||
|
||||
```
|
||||
main/src/local-backend/BackendManager.js
|
||||
- Add deleteColumn() if implementing column deletion
|
||||
- Add deleteTable() IPC handler
|
||||
|
||||
panels/schemamanager/SchemaPanel.tsx
|
||||
- Add showCreateTableModal state
|
||||
- Wire up modal
|
||||
|
||||
panels/schemamanager/TableRow.tsx
|
||||
- Add AddColumnForm integration
|
||||
- Add column edit/delete actions
|
||||
- Add table delete action
|
||||
|
||||
panels/databrowser/DataBrowser.tsx
|
||||
- Improve empty state with link to Schema panel
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Estimation
|
||||
|
||||
| Part | Complexity | Time Est |
|
||||
| --------------------- | ---------- | ----------- |
|
||||
| CreateTableModal | Medium | 1-2 hrs |
|
||||
| AddColumnForm | Medium | 1 hr |
|
||||
| ColumnEditor (rename) | Simple | 30 min |
|
||||
| Delete table | Simple | 30 min |
|
||||
| Empty states | Simple | 15 min |
|
||||
| Testing & polish | - | 1 hr |
|
||||
| **Total** | - | **4-5 hrs** |
|
||||
|
||||
---
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] Can create a new table with name and columns
|
||||
- [ ] Can add columns to existing tables
|
||||
- [ ] Can rename columns
|
||||
- [ ] Can delete tables (with confirmation)
|
||||
- [ ] Field types have proper UI controls
|
||||
- [ ] Empty states guide users to create structures
|
||||
- [ ] All actions refresh the panel after success
|
||||
- [ ] Error states for invalid names/constraints
|
||||
- [ ] Theme-compliant styling (no hardcoded colors)
|
||||
|
||||
---
|
||||
|
||||
## Technical Notes
|
||||
|
||||
### Table Name Validation
|
||||
|
||||
```javascript
|
||||
const isValidTableName = (name) => {
|
||||
// Must start with letter, alphanumeric + underscore only
|
||||
// Cannot be SQLite reserved words
|
||||
return /^[a-zA-Z][a-zA-Z0-9_]*$/.test(name) && !SQLITE_RESERVED_WORDS.includes(name.toUpperCase());
|
||||
};
|
||||
```
|
||||
|
||||
### Default Values for Types
|
||||
|
||||
```javascript
|
||||
const getDefaultForType = (type) =>
|
||||
({
|
||||
String: '',
|
||||
Number: 0,
|
||||
Boolean: false,
|
||||
Date: new Date().toISOString(),
|
||||
Object: {},
|
||||
Array: []
|
||||
}[type]);
|
||||
```
|
||||
|
||||
### SQLite Limitations
|
||||
|
||||
- No DROP COLUMN (before SQLite 3.35.0)
|
||||
- No ALTER COLUMN TYPE
|
||||
- RENAME COLUMN supported (SQLite 3.25.0+)
|
||||
|
||||
---
|
||||
|
||||
## Related Tasks
|
||||
|
||||
- **007H**: Schema Panel (viewing) ✅
|
||||
- **007I**: Data Browser (viewing/editing) ✅
|
||||
- **007J**: Schema & Data Creation UX ← This task
|
||||
@@ -0,0 +1,120 @@
|
||||
# TASK-007K: Local Backend Bug Fixes & Polish
|
||||
|
||||
## Status: 📋 DRAFT
|
||||
|
||||
## Overview
|
||||
|
||||
This task addresses the known bugs and UX issues from TASK-007J. The Schema Manager and Data Browser are functional but have rough edges that need polish.
|
||||
|
||||
## Priority 1: Critical Bugs
|
||||
|
||||
### 1.1 Object/Array Field Editor
|
||||
|
||||
**Problem:** Can't type into Object/Array field text areas in CellEditor.
|
||||
|
||||
**Files:** `CellEditor.tsx`
|
||||
|
||||
**Fix:** Review the textarea handling, may need to prevent event bubbling or fix focus management.
|
||||
|
||||
### 1.2 Real SQLite Database
|
||||
|
||||
**Problem:** Falls back to in-memory mock because better-sqlite3 import fails in Electron renderer.
|
||||
|
||||
**Files:** `LocalSQLAdapter.js`, `BackendManager.js`
|
||||
|
||||
**Fix Options:**
|
||||
|
||||
- Move SQLite operations to main process via IPC
|
||||
- Use sql.js (pure JS SQLite) in renderer
|
||||
- Configure better-sqlite3 for Electron properly
|
||||
|
||||
### 1.3 Schema Persistence
|
||||
|
||||
**Problem:** Schema changes (new tables, columns) don't persist across restarts.
|
||||
|
||||
**Files:** `SchemaManager.js`, `LocalBackendServer.js`
|
||||
|
||||
**Fix:** Ensure schema table is properly created and migrations are stored.
|
||||
|
||||
## Priority 2: Missing Features
|
||||
|
||||
### 2.1 Edit Existing Tables
|
||||
|
||||
**Problem:** No UI for adding/removing columns from existing tables.
|
||||
|
||||
**Files:** `SchemaPanel.tsx`, `TableRow.tsx`
|
||||
|
||||
**Add:**
|
||||
|
||||
- "Add Column" button in expanded table row
|
||||
- Delete column button per column
|
||||
- Confirmation for destructive actions
|
||||
|
||||
### 2.2 Delete Tables
|
||||
|
||||
**Problem:** No way to delete a table.
|
||||
|
||||
**Files:** `SchemaPanel.tsx`, `TableRow.tsx`
|
||||
|
||||
**Add:**
|
||||
|
||||
- Delete button in table row
|
||||
- Confirmation dialog
|
||||
- Backend `backend:deleteTable` IPC handler
|
||||
|
||||
### 2.3 Better Boolean Toggle
|
||||
|
||||
**Problem:** Checkbox not intuitive for boolean fields.
|
||||
|
||||
**Files:** `CellEditor.tsx`
|
||||
|
||||
**Add:** Toggle switch component instead of checkbox.
|
||||
|
||||
### 2.4 Date Picker
|
||||
|
||||
**Problem:** Text input for dates is error-prone.
|
||||
|
||||
**Files:** `CellEditor.tsx`
|
||||
|
||||
**Add:** Date picker component (can use core-ui DateInput if available).
|
||||
|
||||
## Priority 3: UX Improvements
|
||||
|
||||
### 3.1 Pointer/Relation Field Editor
|
||||
|
||||
Add dropdown to select from related records.
|
||||
|
||||
### 3.2 File Field Editor
|
||||
|
||||
Add file picker/upload UI.
|
||||
|
||||
### 3.3 Search All Fields
|
||||
|
||||
Extend search to Number, Date fields (not just String).
|
||||
|
||||
### 3.4 Keyboard Navigation
|
||||
|
||||
- Arrow keys to navigate grid
|
||||
- Enter to edit cell
|
||||
- Escape to cancel
|
||||
- Tab to move between cells
|
||||
|
||||
## Estimated Effort
|
||||
|
||||
| Priority | Items | Effort |
|
||||
| --------- | ----- | ------------- |
|
||||
| P1 | 3 | 4-6 hrs |
|
||||
| P2 | 4 | 3-4 hrs |
|
||||
| P3 | 4 | 4-6 hrs |
|
||||
| **Total** | | **11-16 hrs** |
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Core UI components (Toggle, DatePicker)
|
||||
- May need main process changes for SQLite
|
||||
|
||||
## Notes
|
||||
|
||||
- Consider splitting this into multiple sub-tasks if scope is too large
|
||||
- SQLite issue may require significant architecture change
|
||||
- Focus on P1 bugs first for usable MVP
|
||||
@@ -71,13 +71,15 @@
|
||||
|
||||
## Recent Updates
|
||||
|
||||
| Date | Update |
|
||||
| ---------- | --------------------------------------------------------- |
|
||||
| 2026-01-15 | TASK-007 Integrated Backend core infrastructure complete |
|
||||
| 2026-01-07 | Corrected PROGRESS.md to reflect actual completion status |
|
||||
| 2025-12-30 | TASK-002 bug fixes and system table support |
|
||||
| 2025-12-29 | TASK-001 Backend Services Panel completed |
|
||||
| 2025-12-29 | TASK-002 Data Nodes completed |
|
||||
| Date | Update |
|
||||
| ---------- | ----------------------------------------------------------- |
|
||||
| 2026-01-15 | TASK-007J Schema Manager & Data Browser UX complete w/ bugs |
|
||||
| 2026-01-15 | TASK-007K Draft created for bug fixes |
|
||||
| 2026-01-15 | TASK-007 Integrated Backend core infrastructure complete |
|
||||
| 2026-01-07 | Corrected PROGRESS.md to reflect actual completion status |
|
||||
| 2025-12-30 | TASK-002 bug fixes and system table support |
|
||||
| 2025-12-29 | TASK-001 Backend Services Panel completed |
|
||||
| 2025-12-29 | TASK-002 Data Nodes completed |
|
||||
|
||||
---
|
||||
|
||||
@@ -103,7 +105,7 @@
|
||||
|
||||
### TASK-007: Integrated Local Backend ✅
|
||||
|
||||
Zero-config local SQLite backend system - infrastructure complete.
|
||||
Zero-config local SQLite backend system - infrastructure complete with Schema Manager & Data Browser UI.
|
||||
|
||||
**Completed subtasks:**
|
||||
|
||||
@@ -111,6 +113,9 @@ Zero-config local SQLite backend system - infrastructure complete.
|
||||
- TASK-007B: Local Backend Server (Express + IPC handlers)
|
||||
- TASK-007C: WorkflowRunner (Cloud function execution)
|
||||
- TASK-007D: Launcher UI (BackendServicesPanel integration)
|
||||
- TASK-007H: Backend Manager IPC Handlers
|
||||
- TASK-007I: Data Browser Panel (spreadsheet-style grid)
|
||||
- TASK-007J: Schema Manager Panel (create tables, add columns)
|
||||
|
||||
**What's working:**
|
||||
|
||||
@@ -118,12 +123,16 @@ Zero-config local SQLite backend system - infrastructure complete.
|
||||
- Start/stop backend servers via IPC
|
||||
- REST API compatible with Parse Server
|
||||
- Auto-schema (tables/columns created on first write)
|
||||
- Schema Manager: create tables, add columns, view schema
|
||||
- Data Browser: view records, inline editing, create/delete records
|
||||
- UUID `id` field (compatible with Noodl frontend objects)
|
||||
|
||||
**Future work (separate task):**
|
||||
**Known Issues (TASK-007K):**
|
||||
|
||||
- Schema viewer/editor panel for visual data management
|
||||
- Data browser with grid view
|
||||
- Migration/export tools (optional)
|
||||
- Object/Array field editors don't work
|
||||
- Real SQLite falls back to in-memory mock (better-sqlite3 issues)
|
||||
- Can't edit/delete existing tables
|
||||
- Cell editor focus issues
|
||||
|
||||
**Implementation Files:**
|
||||
|
||||
@@ -143,6 +152,20 @@ packages/noodl-editor/src/main/src/local-backend/
|
||||
packages/noodl-editor/src/editor/src/views/panels/BackendServicesPanel/
|
||||
├── hooks/useLocalBackends.ts
|
||||
└── LocalBackendCard/LocalBackendCard.tsx
|
||||
|
||||
packages/noodl-editor/src/editor/src/views/panels/schemamanager/
|
||||
├── SchemaPanel.tsx, SchemaPanel.module.scss
|
||||
├── TableRow.tsx, TableRow.module.scss
|
||||
├── CreateTableModal.tsx, CreateTableModal.module.scss
|
||||
├── AddColumnForm.tsx, AddColumnForm.module.scss
|
||||
└── index.ts
|
||||
|
||||
packages/noodl-editor/src/editor/src/views/panels/databrowser/
|
||||
├── DataBrowser.tsx, DataBrowser.module.scss
|
||||
├── DataGrid.tsx, DataGrid.module.scss
|
||||
├── CellEditor.tsx, CellEditor.module.scss
|
||||
├── NewRecordModal.tsx, NewRecordModal.module.scss
|
||||
└── index.ts
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Reference in New Issue
Block a user