Skip to content
76 changes: 74 additions & 2 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@
> **Spec Version:** @objectstack/spec v3.0.9
> **Client Version:** @objectstack/client v3.0.9
> **Target UX Benchmark:** 🎯 Airtable parity
> **Current Priority:** AppShell Navigation · Designer Interaction · View Config Live Preview Sync · Dashboard Config Panel · Airtable UX Polish · **Flow Designer ✅** · **Schema-Driven View Config Panel ✅**
> **Current Priority:** AppShell Navigation · Designer Interaction · View Config Live Preview Sync · Dashboard Config Panel · Airtable UX Polish · **Flow Designer ✅** · **App Creation & Editing Flow ✅**

---

## 📋 Executive Summary

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.

**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.
**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.

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

### P1.11 App Creation & Editing Flow (Airtable Interface Designer UX)

> Full app creation & editing experience aligned with Airtable Interface Designer UX.
> Components live in `@object-ui/plugin-designer`, types in `@object-ui/types`.

**Types & Interfaces:**
- [x] `AppWizardDraft` / `AppWizardStep` / `BrandingConfig` / `ObjectSelection` / `EditorMode` types
- [x] `isValidAppName()` snake_case validator function
- [x] `wizardDraftToAppSchema()` draft-to-schema conversion function

**App Creation Wizard (4-step):**
- [x] Step 1: Basic Info — name (snake_case validated), title, description, icon, template, layout selector
- [x] Step 2: Object Selection — card grid with search, select all/none, toggle selection
- [x] Step 3: Navigation Builder — auto-generates NavigationItem[] from selected objects, add group/URL/separator, reorder up/down, remove
- [x] Step 4: Branding — logo URL, primary color, favicon, live preview card
- [x] Step indicator with connected progress dots
- [x] Step validation (step 1 requires valid snake_case name + title)
- [x] onComplete callback returns full AppWizardDraft

**Navigation Designer:**
- [x] Recursive NavigationItem tree renderer (supports infinite nesting)
- [x] Quick add buttons for all 7 nav item types (object, dashboard, page, report, group, URL, separator)
- [x] Inline label editing (double-click to edit, Enter/Escape to commit/discard)
- [x] Add child to groups (nested navigation)
- [x] Reorder items (up/down buttons)
- [x] Deep tree operations (remove/reorder works at any depth)
- [x] Live preview sidebar showing navigation as rendered
- [x] Type badges with color coding

**Page Canvas Editor:**
- [x] Component palette (Grid, Kanban, Calendar, Gallery, Dashboard, Form, Layout Grid)
- [x] Component list with drag handles, selection, reorder
- [x] Property panel for selected component (label, type, ID)
- [x] Add/remove/reorder components
- [x] Syncs PageSchema children on every change

**Dashboard Editor:**
- [x] 6 widget types (KPI Metric, Bar Chart, Line Chart, Pie Chart, Table, Grid)
- [x] Widget card grid with selection, drag handles, reorder
- [x] Widget property panel (title, type, data source, value field, aggregate, color variant)
- [x] Add/remove/reorder widgets
- [x] Grid layout based on DashboardSchema columns

**Object View Configurator:**
- [x] 7 view type switcher (Grid, Kanban, Calendar, Gallery, Timeline, Map, Gantt)
- [x] Column visibility toggle with visible count
- [x] Column reorder (up/down)
- [x] Toolbar toggles (showSearch, showFilters, showSort)
- [x] Appearance section (row height, striped, bordered)
- [x] Collapsible sections

**Editor Mode Toggle:**
- [x] Three-way toggle (Edit / Preview / Code)
- [x] Radio group accessibility (role="radiogroup", aria-checked)
- [x] Disabled state support

**i18n:**
- [x] `appDesigner` section with 54 keys added to all 10 locales (en, zh, ja, de, fr, es, ar, ru, pt, ko)

**Testing:**
- [x] 9 type tests (isValidAppName, wizardDraftToAppSchema, type shapes)
- [x] 31 AppCreationWizard tests (rendering, steps 1-4, navigation, callbacks, read-only)
- [x] 18 NavigationDesigner tests (rendering, add, remove, groups, badges, read-only)
- [x] 7 EditorModeToggle tests (render, active mode, onChange, accessibility, disabled)
- [x] 14 DashboardEditor tests (rendering, add/remove widgets, property panel, read-only)
- [x] 10 PageCanvasEditor tests (rendering, add/remove components, property panel, read-only)
- [x] 14 ObjectViewConfigurator tests (rendering, view type switch, column visibility, toggles, read-only)
- [x] **Total: 103 new tests, all passing**

**ComponentRegistry:**
- [x] Registered: `app-creation-wizard`, `navigation-designer`, `dashboard-editor`, `page-canvas-editor`, `object-view-configurator`

---

## 🧩 P2 — Polish & Advanced Features
Expand Down
54 changes: 54 additions & 0 deletions packages/i18n/src/locales/ar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,60 @@ const ar = {
general: 'عام',
advanced: 'متقدم',
},
appDesigner: {
createApp: 'Create Application',
editApp: 'Edit Application',
basicInfo: 'Basic Info',
objects: 'Objects',
navigation: 'Navigation',
branding: 'Branding',
appName: 'App Name',
appTitle: 'Title',
appDescription: 'Description',
appIcon: 'Icon',
template: 'Template',
layout: 'Layout',
layoutSidebar: 'Sidebar',
layoutHeader: 'Header',
layoutEmpty: 'Empty',
selectObjects: 'Select Objects',
searchObjects: 'Search objects…',
selectAll: 'Select All',
deselectAll: 'Deselect All',
navBuilder: 'Navigation Builder',
addGroup: 'Add Group',
addUrl: 'Add URL',
addSeparator: 'Add Separator',
noNavItems: 'No navigation items yet.',
logoUrl: 'Logo URL',
primaryColor: 'Primary Color',
faviconUrl: 'Favicon URL',
preview: 'Preview',
complete: 'Complete',
snakeCaseHint: 'Must be snake_case (e.g. my_app)',
modeEdit: 'Edit',
modePreview: 'Preview',
modeCode: 'Code',
addWidget: 'Add Widget',
widgetProperties: 'Widget Properties',
dataSource: 'Data Source',
valueField: 'Value Field',
aggregate: 'Aggregate',
colorVariant: 'Color Variant',
addComponent: 'Add Component',
componentProperties: 'Component Properties',
viewType: 'View Type',
fields: 'Fields',
toolbar: 'Toolbar',
showSearch: 'Show Search',
showFilters: 'Show Filters',
showSort: 'Show Sort',
appearance: 'Appearance',
rowHeight: 'Row Height',
stripedRows: 'Striped Rows',
bordered: 'Bordered',
livePreview: 'Live Preview',
},
console: {
title: 'وحدة تحكم ObjectStack',
initializing: 'جاري تهيئة التطبيق...',
Expand Down
54 changes: 54 additions & 0 deletions packages/i18n/src/locales/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,60 @@ const de = {
general: 'Allgemein',
advanced: 'Erweitert',
},
appDesigner: {
createApp: 'App erstellen',
editApp: 'App bearbeiten',
basicInfo: 'Grundinformationen',
objects: 'Objekte',
navigation: 'Navigation',
branding: 'Branding',
appName: 'App-Name',
appTitle: 'Titel',
appDescription: 'Beschreibung',
appIcon: 'Symbol',
template: 'Vorlage',
layout: 'Layout',
layoutSidebar: 'Seitenleiste',
layoutHeader: 'Kopfzeile',
layoutEmpty: 'Leer',
selectObjects: 'Objekte auswählen',
searchObjects: 'Objekte suchen…',
selectAll: 'Alle auswählen',
deselectAll: 'Alle abwählen',
navBuilder: 'Navigations-Builder',
addGroup: 'Gruppe hinzufügen',
addUrl: 'URL hinzufügen',
addSeparator: 'Trenner hinzufügen',
noNavItems: 'Noch keine Navigationselemente.',
logoUrl: 'Logo-URL',
primaryColor: 'Primärfarbe',
faviconUrl: 'Favicon-URL',
preview: 'Vorschau',
complete: 'Fertigstellen',
snakeCaseHint: 'Muss snake_case sein (z.B. my_app)',
modeEdit: 'Bearbeiten',
modePreview: 'Vorschau',
modeCode: 'Code',
addWidget: 'Widget hinzufügen',
widgetProperties: 'Widget-Eigenschaften',
dataSource: 'Datenquelle',
valueField: 'Wertfeld',
aggregate: 'Aggregat',
colorVariant: 'Farbvariante',
addComponent: 'Komponente hinzufügen',
componentProperties: 'Komponenteneigenschaften',
viewType: 'Ansichtstyp',
fields: 'Felder',
toolbar: 'Symbolleiste',
showSearch: 'Suche anzeigen',
showFilters: 'Filter anzeigen',
showSort: 'Sortierung anzeigen',
appearance: 'Erscheinung',
rowHeight: 'Zeilenhöhe',
stripedRows: 'Gestreifte Zeilen',
bordered: 'Umrandet',
livePreview: 'Live-Vorschau',
},
console: {
title: 'ObjectStack Konsole',
initializing: 'Anwendung wird initialisiert...',
Expand Down
54 changes: 54 additions & 0 deletions packages/i18n/src/locales/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,60 @@ const en = {
general: 'General',
advanced: 'Advanced',
},
appDesigner: {
createApp: 'Create Application',
editApp: 'Edit Application',
basicInfo: 'Basic Info',
objects: 'Objects',
navigation: 'Navigation',
branding: 'Branding',
appName: 'App Name',
appTitle: 'Title',
appDescription: 'Description',
appIcon: 'Icon',
template: 'Template',
layout: 'Layout',
layoutSidebar: 'Sidebar',
layoutHeader: 'Header',
layoutEmpty: 'Empty',
selectObjects: 'Select Objects',
searchObjects: 'Search objects…',
selectAll: 'Select All',
deselectAll: 'Deselect All',
navBuilder: 'Navigation Builder',
addGroup: 'Add Group',
addUrl: 'Add URL',
addSeparator: 'Add Separator',
noNavItems: 'No navigation items yet.',
logoUrl: 'Logo URL',
primaryColor: 'Primary Color',
faviconUrl: 'Favicon URL',
preview: 'Preview',
complete: 'Complete',
snakeCaseHint: 'Must be snake_case (e.g. my_app)',
modeEdit: 'Edit',
modePreview: 'Preview',
modeCode: 'Code',
addWidget: 'Add Widget',
widgetProperties: 'Widget Properties',
dataSource: 'Data Source',
valueField: 'Value Field',
aggregate: 'Aggregate',
colorVariant: 'Color Variant',
addComponent: 'Add Component',
componentProperties: 'Component Properties',
viewType: 'View Type',
fields: 'Fields',
toolbar: 'Toolbar',
showSearch: 'Show Search',
showFilters: 'Show Filters',
showSort: 'Show Sort',
appearance: 'Appearance',
rowHeight: 'Row Height',
stripedRows: 'Striped Rows',
bordered: 'Bordered',
livePreview: 'Live Preview',
},
console: {
title: 'ObjectStack Console',
initializing: 'Initializing application...',
Expand Down
54 changes: 54 additions & 0 deletions packages/i18n/src/locales/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,60 @@ const es = {
general: 'General',
advanced: 'Avanzado',
},
appDesigner: {
createApp: 'Crear aplicación',
editApp: 'Editar aplicación',
basicInfo: 'Información básica',
objects: 'Objetos',
navigation: 'Navegación',
branding: 'Marca',
appName: 'Nombre de la app',
appTitle: 'Título',
appDescription: 'Descripción',
appIcon: 'Ícono',
template: 'Plantilla',
layout: 'Diseño',
layoutSidebar: 'Barra lateral',
layoutHeader: 'Encabezado',
layoutEmpty: 'Vacío',
selectObjects: 'Seleccionar objetos',
searchObjects: 'Buscar objetos…',
selectAll: 'Seleccionar todo',
deselectAll: 'Deseleccionar todo',
navBuilder: 'Constructor de navegación',
addGroup: 'Agregar grupo',
addUrl: 'Agregar URL',
addSeparator: 'Agregar separador',
noNavItems: 'Sin elementos de navegación.',
logoUrl: 'URL del logo',
primaryColor: 'Color principal',
faviconUrl: 'URL del favicon',
preview: 'Vista previa',
complete: 'Completar',
snakeCaseHint: 'Debe ser snake_case (ej. my_app)',
modeEdit: 'Editar',
modePreview: 'Vista previa',
modeCode: 'Código',
addWidget: 'Agregar widget',
widgetProperties: 'Propiedades del widget',
dataSource: 'Fuente de datos',
valueField: 'Campo de valor',
aggregate: 'Agregado',
colorVariant: 'Variante de color',
addComponent: 'Agregar componente',
componentProperties: 'Propiedades del componente',
viewType: 'Tipo de vista',
fields: 'Campos',
toolbar: 'Barra de herramientas',
showSearch: 'Mostrar búsqueda',
showFilters: 'Mostrar filtros',
showSort: 'Mostrar orden',
appearance: 'Apariencia',
rowHeight: 'Altura de fila',
stripedRows: 'Filas rayadas',
bordered: 'Con borde',
livePreview: 'Vista previa en vivo',
},
console: {
title: 'Consola ObjectStack',
initializing: 'Inicializando aplicación...',
Expand Down
Loading
Loading