Skip to content

Commit 4e3e153

Browse files
authored
Merge pull request #739 from objectstack-ai/copilot/implement-app-creation-flow
feat: App Creation & Editing Flow (Airtable Designer UX)
2 parents 18d479d + 8099d4f commit 4e3e153

27 files changed

+4701
-4
lines changed

ROADMAP.md

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@
55
> **Spec Version:** @objectstack/spec v3.0.9
66
> **Client Version:** @objectstack/client v3.0.9
77
> **Target UX Benchmark:** 🎯 Airtable parity
8-
> **Current Priority:** AppShell Navigation · Designer Interaction · View Config Live Preview Sync · Dashboard Config Panel · Airtable UX Polish · **Flow Designer ✅** · **Schema-Driven View Config Panel**
8+
> **Current Priority:** AppShell Navigation · Designer Interaction · View Config Live Preview Sync · Dashboard Config Panel · Airtable UX Polish · **Flow Designer ✅** · **App Creation & Editing Flow**
99
1010
---
1111

1212
## 📋 Executive Summary
1313

1414
ObjectUI is a universal Server-Driven UI (SDUI) engine built on React + Tailwind + Shadcn. It renders JSON metadata from the @objectstack/spec protocol into pixel-perfect, accessible, and interactive enterprise interfaces.
1515

16-
**Where We Are:** Foundation is **solid and shipping** — 35 packages, 99+ components, 5,618+ tests, 78 Storybook stories, 42/42 builds passing, ~88% protocol alignment. SpecBridge, Expression Engine, Action Engine, data binding, all view plugins (Grid/Kanban/Calendar/Gantt/Timeline/Map/Gallery), Record components, Report engine, Dashboard BI features, mobile UX, i18n (11 locales), WCAG AA accessibility, Designer Phase 1 (ViewDesigner drag-to-reorder ✅), Console through Phase 20 (L3), **AppShell Navigation Renderer** (P0.1), **Flow Designer** (P2.4), **Feed/Chatter UI** (P1.5), and **ListView Spec Protocol Gap Fixes** (P2.6 partial) — all ✅ complete.
16+
**Where We Are:** Foundation is **solid and shipping** — 35 packages, 99+ components, 5,700+ tests, 78 Storybook stories, 42/42 builds passing, ~85% protocol alignment. SpecBridge, Expression Engine, Action Engine, data binding, all view plugins (Grid/Kanban/Calendar/Gantt/Timeline/Map/Gallery), Record components, Report engine, Dashboard BI features, mobile UX, i18n (11 locales), WCAG AA accessibility, Designer Phase 1 (ViewDesigner drag-to-reorder ✅), Console through Phase 20 (L3), **AppShell Navigation Renderer** (P0.1), **Flow Designer** (P2.4), **Feed/Chatter UI** (P1.5), and **App Creation & Editing Flow** (P1.11) — all ✅ complete.
1717

1818
**What Remains:** The gap to **Airtable-level UX** is primarily in:
1919
1. ~~**AppShell** — No dynamic navigation renderer from spec JSON (last P0 blocker)~~ ✅ Complete
@@ -389,6 +389,78 @@ ObjectUI is a universal Server-Driven UI (SDUI) engine built on React + Tailwind
389389
- [ ] Mobile/tablet end-to-end testing for all view types
390390
- [ ] Dynamic width calculation for Gantt task list and Kanban columns based on container width
391391

392+
### P1.11 App Creation & Editing Flow (Airtable Interface Designer UX)
393+
394+
> Full app creation & editing experience aligned with Airtable Interface Designer UX.
395+
> Components live in `@object-ui/plugin-designer`, types in `@object-ui/types`.
396+
397+
**Types & Interfaces:**
398+
- [x] `AppWizardDraft` / `AppWizardStep` / `BrandingConfig` / `ObjectSelection` / `EditorMode` types
399+
- [x] `isValidAppName()` snake_case validator function
400+
- [x] `wizardDraftToAppSchema()` draft-to-schema conversion function
401+
402+
**App Creation Wizard (4-step):**
403+
- [x] Step 1: Basic Info — name (snake_case validated), title, description, icon, template, layout selector
404+
- [x] Step 2: Object Selection — card grid with search, select all/none, toggle selection
405+
- [x] Step 3: Navigation Builder — auto-generates NavigationItem[] from selected objects, add group/URL/separator, reorder up/down, remove
406+
- [x] Step 4: Branding — logo URL, primary color, favicon, live preview card
407+
- [x] Step indicator with connected progress dots
408+
- [x] Step validation (step 1 requires valid snake_case name + title)
409+
- [x] onComplete callback returns full AppWizardDraft
410+
411+
**Navigation Designer:**
412+
- [x] Recursive NavigationItem tree renderer (supports infinite nesting)
413+
- [x] Quick add buttons for all 7 nav item types (object, dashboard, page, report, group, URL, separator)
414+
- [x] Inline label editing (double-click to edit, Enter/Escape to commit/discard)
415+
- [x] Add child to groups (nested navigation)
416+
- [x] Reorder items (up/down buttons)
417+
- [x] Deep tree operations (remove/reorder works at any depth)
418+
- [x] Live preview sidebar showing navigation as rendered
419+
- [x] Type badges with color coding
420+
421+
**Page Canvas Editor:**
422+
- [x] Component palette (Grid, Kanban, Calendar, Gallery, Dashboard, Form, Layout Grid)
423+
- [x] Component list with drag handles, selection, reorder
424+
- [x] Property panel for selected component (label, type, ID)
425+
- [x] Add/remove/reorder components
426+
- [x] Syncs PageSchema children on every change
427+
428+
**Dashboard Editor:**
429+
- [x] 6 widget types (KPI Metric, Bar Chart, Line Chart, Pie Chart, Table, Grid)
430+
- [x] Widget card grid with selection, drag handles, reorder
431+
- [x] Widget property panel (title, type, data source, value field, aggregate, color variant)
432+
- [x] Add/remove/reorder widgets
433+
- [x] Grid layout based on DashboardSchema columns
434+
435+
**Object View Configurator:**
436+
- [x] 7 view type switcher (Grid, Kanban, Calendar, Gallery, Timeline, Map, Gantt)
437+
- [x] Column visibility toggle with visible count
438+
- [x] Column reorder (up/down)
439+
- [x] Toolbar toggles (showSearch, showFilters, showSort)
440+
- [x] Appearance section (row height, striped, bordered)
441+
- [x] Collapsible sections
442+
443+
**Editor Mode Toggle:**
444+
- [x] Three-way toggle (Edit / Preview / Code)
445+
- [x] Radio group accessibility (role="radiogroup", aria-checked)
446+
- [x] Disabled state support
447+
448+
**i18n:**
449+
- [x] `appDesigner` section with 54 keys added to all 10 locales (en, zh, ja, de, fr, es, ar, ru, pt, ko)
450+
451+
**Testing:**
452+
- [x] 9 type tests (isValidAppName, wizardDraftToAppSchema, type shapes)
453+
- [x] 31 AppCreationWizard tests (rendering, steps 1-4, navigation, callbacks, read-only)
454+
- [x] 18 NavigationDesigner tests (rendering, add, remove, groups, badges, read-only)
455+
- [x] 7 EditorModeToggle tests (render, active mode, onChange, accessibility, disabled)
456+
- [x] 14 DashboardEditor tests (rendering, add/remove widgets, property panel, read-only)
457+
- [x] 10 PageCanvasEditor tests (rendering, add/remove components, property panel, read-only)
458+
- [x] 14 ObjectViewConfigurator tests (rendering, view type switch, column visibility, toggles, read-only)
459+
- [x] **Total: 103 new tests, all passing**
460+
461+
**ComponentRegistry:**
462+
- [x] Registered: `app-creation-wizard`, `navigation-designer`, `dashboard-editor`, `page-canvas-editor`, `object-view-configurator`
463+
392464
---
393465

394466
## 🧩 P2 — Polish & Advanced Features

packages/i18n/src/locales/ar.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,60 @@ const ar = {
138138
general: 'عام',
139139
advanced: 'متقدم',
140140
},
141+
appDesigner: {
142+
createApp: 'Create Application',
143+
editApp: 'Edit Application',
144+
basicInfo: 'Basic Info',
145+
objects: 'Objects',
146+
navigation: 'Navigation',
147+
branding: 'Branding',
148+
appName: 'App Name',
149+
appTitle: 'Title',
150+
appDescription: 'Description',
151+
appIcon: 'Icon',
152+
template: 'Template',
153+
layout: 'Layout',
154+
layoutSidebar: 'Sidebar',
155+
layoutHeader: 'Header',
156+
layoutEmpty: 'Empty',
157+
selectObjects: 'Select Objects',
158+
searchObjects: 'Search objects…',
159+
selectAll: 'Select All',
160+
deselectAll: 'Deselect All',
161+
navBuilder: 'Navigation Builder',
162+
addGroup: 'Add Group',
163+
addUrl: 'Add URL',
164+
addSeparator: 'Add Separator',
165+
noNavItems: 'No navigation items yet.',
166+
logoUrl: 'Logo URL',
167+
primaryColor: 'Primary Color',
168+
faviconUrl: 'Favicon URL',
169+
preview: 'Preview',
170+
complete: 'Complete',
171+
snakeCaseHint: 'Must be snake_case (e.g. my_app)',
172+
modeEdit: 'Edit',
173+
modePreview: 'Preview',
174+
modeCode: 'Code',
175+
addWidget: 'Add Widget',
176+
widgetProperties: 'Widget Properties',
177+
dataSource: 'Data Source',
178+
valueField: 'Value Field',
179+
aggregate: 'Aggregate',
180+
colorVariant: 'Color Variant',
181+
addComponent: 'Add Component',
182+
componentProperties: 'Component Properties',
183+
viewType: 'View Type',
184+
fields: 'Fields',
185+
toolbar: 'Toolbar',
186+
showSearch: 'Show Search',
187+
showFilters: 'Show Filters',
188+
showSort: 'Show Sort',
189+
appearance: 'Appearance',
190+
rowHeight: 'Row Height',
191+
stripedRows: 'Striped Rows',
192+
bordered: 'Bordered',
193+
livePreview: 'Live Preview',
194+
},
141195
console: {
142196
title: 'وحدة تحكم ObjectStack',
143197
initializing: 'جاري تهيئة التطبيق...',

packages/i18n/src/locales/de.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,60 @@ const de = {
137137
general: 'Allgemein',
138138
advanced: 'Erweitert',
139139
},
140+
appDesigner: {
141+
createApp: 'App erstellen',
142+
editApp: 'App bearbeiten',
143+
basicInfo: 'Grundinformationen',
144+
objects: 'Objekte',
145+
navigation: 'Navigation',
146+
branding: 'Branding',
147+
appName: 'App-Name',
148+
appTitle: 'Titel',
149+
appDescription: 'Beschreibung',
150+
appIcon: 'Symbol',
151+
template: 'Vorlage',
152+
layout: 'Layout',
153+
layoutSidebar: 'Seitenleiste',
154+
layoutHeader: 'Kopfzeile',
155+
layoutEmpty: 'Leer',
156+
selectObjects: 'Objekte auswählen',
157+
searchObjects: 'Objekte suchen…',
158+
selectAll: 'Alle auswählen',
159+
deselectAll: 'Alle abwählen',
160+
navBuilder: 'Navigations-Builder',
161+
addGroup: 'Gruppe hinzufügen',
162+
addUrl: 'URL hinzufügen',
163+
addSeparator: 'Trenner hinzufügen',
164+
noNavItems: 'Noch keine Navigationselemente.',
165+
logoUrl: 'Logo-URL',
166+
primaryColor: 'Primärfarbe',
167+
faviconUrl: 'Favicon-URL',
168+
preview: 'Vorschau',
169+
complete: 'Fertigstellen',
170+
snakeCaseHint: 'Muss snake_case sein (z.B. my_app)',
171+
modeEdit: 'Bearbeiten',
172+
modePreview: 'Vorschau',
173+
modeCode: 'Code',
174+
addWidget: 'Widget hinzufügen',
175+
widgetProperties: 'Widget-Eigenschaften',
176+
dataSource: 'Datenquelle',
177+
valueField: 'Wertfeld',
178+
aggregate: 'Aggregat',
179+
colorVariant: 'Farbvariante',
180+
addComponent: 'Komponente hinzufügen',
181+
componentProperties: 'Komponenteneigenschaften',
182+
viewType: 'Ansichtstyp',
183+
fields: 'Felder',
184+
toolbar: 'Symbolleiste',
185+
showSearch: 'Suche anzeigen',
186+
showFilters: 'Filter anzeigen',
187+
showSort: 'Sortierung anzeigen',
188+
appearance: 'Erscheinung',
189+
rowHeight: 'Zeilenhöhe',
190+
stripedRows: 'Gestreifte Zeilen',
191+
bordered: 'Umrandet',
192+
livePreview: 'Live-Vorschau',
193+
},
140194
console: {
141195
title: 'ObjectStack Konsole',
142196
initializing: 'Anwendung wird initialisiert...',

packages/i18n/src/locales/en.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,60 @@ const en = {
137137
general: 'General',
138138
advanced: 'Advanced',
139139
},
140+
appDesigner: {
141+
createApp: 'Create Application',
142+
editApp: 'Edit Application',
143+
basicInfo: 'Basic Info',
144+
objects: 'Objects',
145+
navigation: 'Navigation',
146+
branding: 'Branding',
147+
appName: 'App Name',
148+
appTitle: 'Title',
149+
appDescription: 'Description',
150+
appIcon: 'Icon',
151+
template: 'Template',
152+
layout: 'Layout',
153+
layoutSidebar: 'Sidebar',
154+
layoutHeader: 'Header',
155+
layoutEmpty: 'Empty',
156+
selectObjects: 'Select Objects',
157+
searchObjects: 'Search objects…',
158+
selectAll: 'Select All',
159+
deselectAll: 'Deselect All',
160+
navBuilder: 'Navigation Builder',
161+
addGroup: 'Add Group',
162+
addUrl: 'Add URL',
163+
addSeparator: 'Add Separator',
164+
noNavItems: 'No navigation items yet.',
165+
logoUrl: 'Logo URL',
166+
primaryColor: 'Primary Color',
167+
faviconUrl: 'Favicon URL',
168+
preview: 'Preview',
169+
complete: 'Complete',
170+
snakeCaseHint: 'Must be snake_case (e.g. my_app)',
171+
modeEdit: 'Edit',
172+
modePreview: 'Preview',
173+
modeCode: 'Code',
174+
addWidget: 'Add Widget',
175+
widgetProperties: 'Widget Properties',
176+
dataSource: 'Data Source',
177+
valueField: 'Value Field',
178+
aggregate: 'Aggregate',
179+
colorVariant: 'Color Variant',
180+
addComponent: 'Add Component',
181+
componentProperties: 'Component Properties',
182+
viewType: 'View Type',
183+
fields: 'Fields',
184+
toolbar: 'Toolbar',
185+
showSearch: 'Show Search',
186+
showFilters: 'Show Filters',
187+
showSort: 'Show Sort',
188+
appearance: 'Appearance',
189+
rowHeight: 'Row Height',
190+
stripedRows: 'Striped Rows',
191+
bordered: 'Bordered',
192+
livePreview: 'Live Preview',
193+
},
140194
console: {
141195
title: 'ObjectStack Console',
142196
initializing: 'Initializing application...',

packages/i18n/src/locales/es.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,60 @@ const es = {
137137
general: 'General',
138138
advanced: 'Avanzado',
139139
},
140+
appDesigner: {
141+
createApp: 'Crear aplicación',
142+
editApp: 'Editar aplicación',
143+
basicInfo: 'Información básica',
144+
objects: 'Objetos',
145+
navigation: 'Navegación',
146+
branding: 'Marca',
147+
appName: 'Nombre de la app',
148+
appTitle: 'Título',
149+
appDescription: 'Descripción',
150+
appIcon: 'Ícono',
151+
template: 'Plantilla',
152+
layout: 'Diseño',
153+
layoutSidebar: 'Barra lateral',
154+
layoutHeader: 'Encabezado',
155+
layoutEmpty: 'Vacío',
156+
selectObjects: 'Seleccionar objetos',
157+
searchObjects: 'Buscar objetos…',
158+
selectAll: 'Seleccionar todo',
159+
deselectAll: 'Deseleccionar todo',
160+
navBuilder: 'Constructor de navegación',
161+
addGroup: 'Agregar grupo',
162+
addUrl: 'Agregar URL',
163+
addSeparator: 'Agregar separador',
164+
noNavItems: 'Sin elementos de navegación.',
165+
logoUrl: 'URL del logo',
166+
primaryColor: 'Color principal',
167+
faviconUrl: 'URL del favicon',
168+
preview: 'Vista previa',
169+
complete: 'Completar',
170+
snakeCaseHint: 'Debe ser snake_case (ej. my_app)',
171+
modeEdit: 'Editar',
172+
modePreview: 'Vista previa',
173+
modeCode: 'Código',
174+
addWidget: 'Agregar widget',
175+
widgetProperties: 'Propiedades del widget',
176+
dataSource: 'Fuente de datos',
177+
valueField: 'Campo de valor',
178+
aggregate: 'Agregado',
179+
colorVariant: 'Variante de color',
180+
addComponent: 'Agregar componente',
181+
componentProperties: 'Propiedades del componente',
182+
viewType: 'Tipo de vista',
183+
fields: 'Campos',
184+
toolbar: 'Barra de herramientas',
185+
showSearch: 'Mostrar búsqueda',
186+
showFilters: 'Mostrar filtros',
187+
showSort: 'Mostrar orden',
188+
appearance: 'Apariencia',
189+
rowHeight: 'Altura de fila',
190+
stripedRows: 'Filas rayadas',
191+
bordered: 'Con borde',
192+
livePreview: 'Vista previa en vivo',
193+
},
140194
console: {
141195
title: 'Consola ObjectStack',
142196
initializing: 'Inicializando aplicación...',

0 commit comments

Comments
 (0)