Skip to content

Commit a51e7b9

Browse files
authored
Merge pull request #247 from objectstack-ai/copilot/complete-all-development
2 parents 7cbfa13 + bad6899 commit a51e7b9

21 files changed

+1467
-169
lines changed

DEVELOPMENT_PLAN.md

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -554,16 +554,16 @@ The microkernel architecture (`@objectstack/runtime`) provides:
554554

555555
### 7.3 Current Phase
556556

557-
#### Phase H — @object-ui Driven Development 🔄 IN PROGRESS
557+
#### Phase H — @object-ui Driven Development ✅ COMPLETE
558558

559559
> See [ROADMAP.md](./ROADMAP.md) for full Phase H breakdown.
560560
561561
| # | Task | Priority | Status |
562562
|---|------|:--------:|:------:|
563-
| H.1 | SchemaRenderer integration for business pages | 🔴 | 🔲 |
564-
| H.2 | Metadata-driven navigation | 🔴 | 🔲 |
565-
| H.3 | API client completion (remove mock data reliance) | 🔴 | 🔲 |
566-
| H.4 | @object-ui / @objectos bridge components | 🟡 | 🔲 |
563+
| H.1 | SchemaRenderer integration for business pages | 🔴 | |
564+
| H.2 | Metadata-driven navigation | 🔴 | |
565+
| H.3 | API client completion (remove mock data reliance) | 🔴 | |
566+
| H.4 | @object-ui / @objectos bridge components | 🟡 | |
567567

568568
---
569569

@@ -923,30 +923,30 @@ states:
923923
| G.5 | @objectstack/* packages upgrade to v2.0.7 | 🔴 | ✅ |
924924
| G.6 | Consolidate development plan documents | 🟡 | ✅ |
925925
926-
### 12.3 Phase H — @object-ui Driven Development (Current — February–March 2026)
926+
### 12.3 Phase H — @object-ui Driven Development (Complete — February 2026)
927927
928928
| # | Task | Priority | Status |
929929
|---|------|:--------:|:------:|
930-
| H.1 | SchemaRenderer integration for business pages (grid/form/detail) | 🔴 | 🔲 |
931-
| H.2 | Metadata-driven navigation | 🔴 | 🔲 |
932-
| H.3 | API client completion (remove mock data reliance) | 🔴 | 🔲 |
933-
| H.4 | @object-ui / @objectos bridge components (ObjectPage, ObjectToolbar) | 🟡 | 🔲 |
930+
| H.1 | SchemaRenderer integration for business pages (grid/form/detail/kanban/calendar) | 🔴 | |
931+
| H.2 | Metadata-driven navigation | 🔴 | |
932+
| H.3 | API client completion (remove mock data reliance) | 🔴 | |
933+
| H.4 | @object-ui / @objectos bridge components (ObjectPage, ObjectToolbar, RelatedList, FilterPanel) | 🟡 | |
934934
935935
### 12.4 v1.0.0 — Production Release (Target: March 2026)
936936
937937
| Criterion | Current Status | Required for v1.0 |
938938
|-----------|:-:|:-:|
939939
| All 13 plugins implemented | ✅ | ✅ |
940940
| Spec compliance 100% | ✅ | ✅ |
941-
| Admin Console operational | ✅ 29 pages | ✅ |
941+
| Admin Console operational | ✅ 31 pages | ✅ |
942942
| Security review passed | ✅ | ✅ |
943943
| Integration test suite | ✅ | ✅ |
944944
| Performance baseline (P95 < 100ms) | ✅ | ✅ |
945945
| Docker deployment | ✅ | ✅ |
946946
| E2E smoke tests | ✅ | ✅ |
947947
| Spec Contracts adoption (10/14 plugins) | ✅ | ✅ |
948-
| @object-ui SchemaRenderer for business pages | 🔲 | ✅ |
949-
| Business App Shell with live API data | 🔲 | ✅ |
948+
| @object-ui SchemaRenderer for business pages | | ✅ |
949+
| Business App Shell with live API data | | ✅ |
950950
951951
### 12.5 v1.1.0 — Rich Business UI (Target: April 2026)
952952
@@ -1141,14 +1141,14 @@ Keep Next.js only for `apps/site` (Fumadocs documentation framework dependency).
11411141
| **K** | Offline & Sync | May–Jun 2026 | Service Worker, OPFS storage, mutation queue, conflict resolution UI | 🔲 |
11421142
| **L** | Polish & Performance | Jun–Jul 2026 | Virtual scrolling, skeletons, accessibility (WCAG 2.1 AA), bundle optimization | 🔲 |
11431143

1144-
### Phase H Details (Current Focus)
1144+
### Phase H Details (Complete)
11451145

1146-
| # | Task | Priority | Description |
1147-
|---|------|:--------:|-------------|
1148-
| H.1 | SchemaRenderer for business pages | 🔴 | Replace hand-built views with `<SchemaRenderer view="grid/form/detail" />` |
1149-
| H.2 | Metadata-driven navigation | 🔴 | Sidebar generated from `GET /api/v1/meta/apps` response |
1150-
| H.3 | API client completion | 🔴 | Connect hooks to live @objectstack/client, remove mock data reliance |
1151-
| H.4 | Bridge components | 🟡 | ObjectPage (permissions), ObjectToolbar (view switcher), RelatedList, FilterPanel |
1146+
| # | Task | Priority | Status | Description |
1147+
|---|------|:--------:|:------:|-------------|
1148+
| H.1 | SchemaRenderer for business pages | 🔴 | | Replaced hand-built views with `<SchemaRenderer view="grid/form/detail/kanban/calendar" />` |
1149+
| H.2 | Metadata-driven navigation | 🔴 | | Dynamic sidebar from app metadata, breadcrumbs, recent items |
1150+
| H.3 | API client completion | 🔴 | | Connected hooks to live @objectstack/client, mock data as dev fallback only |
1151+
| H.4 | Bridge components | 🟡 | | ObjectPage (permissions), ObjectToolbar (view switcher), RelatedList, FilterPanel |
11521152

11531153
---
11541154

ROADMAP.md

Lines changed: 40 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
## Executive Summary
1212

13-
ObjectOS is a metadata-driven enterprise runtime platform built on the ObjectStack protocol. With all 13 server-side plugins fully implemented, spec compliance at 100%, and the Admin Console operational with 29 pages, the project is transitioning from **infrastructure build-out** to **UI-centric business application delivery**.
13+
ObjectOS is a metadata-driven enterprise runtime platform built on the ObjectStack protocol. With all 13 server-side plugins fully implemented, spec compliance at 100%, and the Admin Console operational with 31 pages (including record create/edit), Phase H is now complete — the Business App Shell is fully powered by @object-ui SchemaRenderer for metadata-driven UI rendering.
1414

1515
The integration of **@object-ui** (6 packages at v2.0.0) marks a strategic shift: the Admin Console's Business App Shell now leverages @object-ui's `SchemaRenderer` for metadata-driven UI rendering, replacing hand-built components with protocol-compliant controls.
1616

@@ -49,18 +49,20 @@ The integration of **@object-ui** (6 packages at v2.0.0) marks a strategic shift
4949

5050
**Server Metrics**: 21,947 source lines · 107 TypeScript files · 47 test files · 350+ tests
5151

52-
### Frontend — 🔄 Active Development
52+
### Frontend — ✅ Phase H Complete
5353

5454
| Area | Status | Details |
5555
|------|:------:|---------|
5656
| Auth Pages || 6 pages: sign-in, sign-up, forgot-password, reset-password, verify-2fa, home |
5757
| Admin Console || 16 pages: settings, org management, audit, jobs, metrics, plugins, etc. |
58-
| Business App Shell | 🔄 | App page, object list, object record — wired to mock data + API client |
59-
| @object-ui Integration | 🔄 | Packages installed, adapter configured, demo page functional |
60-
| ObjectUI Components | 🔄 | 7 components: DataGrid, MetadataForm, KanbanBoard, ChartWidget, ViewSwitcher, LayoutBuilder, ObjectUIExample |
61-
| Workflow UI | 🔄 | 5 components: WorkflowStatusBadge, ApprovalActions, ActivityTimeline, WorkflowVisualizer, AutomationRulesBuilder |
62-
| Sync UI | 🔄 | 2 components: OfflineIndicator, ConflictResolutionDialog |
63-
| Data Hooks || useRecords (CRUD + optimistic updates), useMetadata, useWorkflow, useSync, useOffline |
58+
| Business App Shell || App page, object list, object record, record create, record edit — powered by SchemaRenderer |
59+
| @object-ui Integration || Packages installed, adapter configured, SchemaRenderer for grid/detail/form/kanban/calendar |
60+
| ObjectUI Components || 11 components: DataGrid, MetadataForm, KanbanBoard, ChartWidget, ViewSwitcher, LayoutBuilder, ObjectUIExample, ObjectPage, ObjectToolbar, RelatedList, FilterPanel |
61+
| Workflow UI || 5 components: WorkflowStatusBadge, ApprovalActions, ActivityTimeline, WorkflowVisualizer, AutomationRulesBuilder |
62+
| Sync UI || 2 components: OfflineIndicator, ConflictResolutionDialog |
63+
| Data Hooks || useRecords (CRUD + optimistic updates + pagination + sorting + filtering), useMetadata, useWorkflow, useSync, useOffline, useRecentItems |
64+
| Navigation || Dynamic sidebar from metadata, breadcrumbs, recent items tracking |
65+
| Error Handling || QueryErrorBoundary with retry capability |
6466

6567
### @object-ui Packages Installed
6668

@@ -110,41 +112,41 @@ Replace hand-built business page views with @object-ui SchemaRenderer.
110112

111113
| # | Task | Priority | Status |
112114
|---|------|:--------:|:------:|
113-
| H.1.1 | Replace `RecordTable` in object-list.tsx with `SchemaRenderer view="grid"` | 🔴 | 🔲 |
114-
| H.1.2 | Replace field detail rendering in object-record.tsx with `SchemaRenderer view="detail"` | 🔴 | 🔲 |
115-
| H.1.3 | Add record creation page using `SchemaRenderer view="form"` | 🔴 | 🔲 |
116-
| H.1.4 | Add record editing using `SchemaRenderer view="form" recordId={id}` | 🔴 | 🔲 |
117-
| H.1.5 | Wire `KanbanBoard` view mode to `SchemaRenderer view="kanban"` | 🟡 | 🔲 |
118-
| H.1.6 | Implement calendar view using `SchemaRenderer view="calendar"` | 🟡 | 🔲 |
115+
| H.1.1 | Replace `RecordTable` in object-list.tsx with `SchemaRenderer view="grid"` | 🔴 | |
116+
| H.1.2 | Replace field detail rendering in object-record.tsx with `SchemaRenderer view="detail"` | 🔴 | |
117+
| H.1.3 | Add record creation page using `SchemaRenderer view="form"` | 🔴 | |
118+
| H.1.4 | Add record editing using `SchemaRenderer view="form" recordId={id}` | 🔴 | |
119+
| H.1.5 | Wire `KanbanBoard` view mode to `SchemaRenderer view="kanban"` | 🟡 | |
120+
| H.1.6 | Implement calendar view using `SchemaRenderer view="calendar"` | 🟡 | |
119121

120122
### H.2 — Metadata-Driven Navigation
121123

122124
| # | Task | Priority | Status |
123125
|---|------|:--------:|:------:|
124-
| H.2.1 | Dynamic sidebar generated from `GET /api/v1/meta/apps` response | 🔴 | 🔲 |
125-
| H.2.2 | Object navigation within apps derived from app metadata | 🔴 | 🔲 |
126-
| H.2.3 | Breadcrumb generation from current route context | 🟡 | 🔲 |
127-
| H.2.4 | Recent items and favorites tracking | 🟢 | 🔲 |
126+
| H.2.1 | Dynamic sidebar generated from `GET /api/v1/meta/apps` response | 🔴 | |
127+
| H.2.2 | Object navigation within apps derived from app metadata | 🔴 | |
128+
| H.2.3 | Breadcrumb generation from current route context | 🟡 | |
129+
| H.2.4 | Recent items and favorites tracking | 🟢 | |
128130

129131
### H.3 — API Client Completion
130132

131133
| # | Task | Priority | Status |
132134
|---|------|:--------:|:------:|
133-
| H.3.1 | Connect useRecords hooks to live `@objectstack/client` API (remove mock fallback reliance) | 🔴 | 🔲 |
134-
| H.3.2 | Implement server-side pagination in object list view | 🔴 | 🔲 |
135-
| H.3.3 | Implement server-side sorting and filtering | 🟡 | 🔲 |
136-
| H.3.4 | Error boundary integration with TanStack Query | 🟡 | 🔲 |
135+
| H.3.1 | Connect useRecords hooks to live `@objectstack/client` API (remove mock fallback reliance) | 🔴 | |
136+
| H.3.2 | Implement server-side pagination in object list view | 🔴 | |
137+
| H.3.3 | Implement server-side sorting and filtering | 🟡 | |
138+
| H.3.4 | Error boundary integration with TanStack Query | 🟡 | |
137139

138140
### H.4 — @object-ui / @objectos Bridge Components
139141

140142
Custom wrapper components that combine @object-ui controls with ObjectOS-specific features.
141143

142144
| # | Task | Priority | Status |
143145
|---|------|:--------:|:------:|
144-
| H.4.1 | `ObjectPage` — wraps SchemaRenderer with ObjectOS permissions check | 🔴 | 🔲 |
145-
| H.4.2 | `ObjectToolbar` — view switcher + new record button + bulk actions | 🟡 | 🔲 |
146-
| H.4.3 | `RelatedList` — displays child/lookup records on detail pages | 🟡 | 🔲 |
147-
| H.4.4 | `FilterPanel` — metadata-aware filter builder for list views | 🟡 | 🔲 |
146+
| H.4.1 | `ObjectPage` — wraps SchemaRenderer with ObjectOS permissions check | 🔴 | |
147+
| H.4.2 | `ObjectToolbar` — view switcher + new record button + bulk actions | 🟡 | |
148+
| H.4.3 | `RelatedList` — displays child/lookup records on detail pages | 🟡 | |
149+
| H.4.4 | `FilterPanel` — metadata-aware filter builder for list views | 🟡 | |
148150

149151
---
150152

@@ -222,8 +224,8 @@ Integrate `@objectos/browser` with the Admin Console for offline-first capabilit
222224
| Performance baseline (P95 < 100ms) ||
223225
| Docker deployment ||
224226
| E2E smoke tests ||
225-
| @object-ui integration (SchemaRenderer for grid/form/detail) | 🔲 Phase H |
226-
| Business App Shell with live API data | 🔲 Phase H |
227+
| @object-ui integration (SchemaRenderer for grid/form/detail) | Phase H |
228+
| Business App Shell with live API data | Phase H |
227229

228230
### v1.1.0 — Rich Business UI (Target: April 2026)
229231

@@ -333,9 +335,17 @@ User Action → React Component → @object-ui/react SchemaRenderer
333335
| Data Adapter | `apps/web/src/lib/object-ui-adapter.ts` | Bridges @object-ui with ObjectStack API |
334336
| API Client | `apps/web/src/lib/api.ts` | @objectstack/client singleton |
335337
| Schema Renderer | `apps/web/src/components/objectui/ObjectUIExample.tsx` | Example integration |
338+
| Object Page | `apps/web/src/components/objectui/ObjectPage.tsx` | Bridge: SchemaRenderer + permissions |
339+
| Object Toolbar | `apps/web/src/components/objectui/ObjectToolbar.tsx` | View switcher + actions |
340+
| Filter Panel | `apps/web/src/components/objectui/FilterPanel.tsx` | Metadata-aware filtering |
341+
| Related List | `apps/web/src/components/objectui/RelatedList.tsx` | Child/lookup records |
336342
| Business App Page | `apps/web/src/pages/apps/app.tsx` | App landing with object cards |
337-
| Object List | `apps/web/src/pages/apps/object-list.tsx` | Records list (to be @object-ui powered) |
338-
| Object Record | `apps/web/src/pages/apps/object-record.tsx` | Record detail (to be @object-ui powered) |
343+
| Object List | `apps/web/src/pages/apps/object-list.tsx` | SchemaRenderer grid/kanban/calendar |
344+
| Object Record | `apps/web/src/pages/apps/object-record.tsx` | SchemaRenderer detail view |
345+
| Record Create | `apps/web/src/pages/apps/record-create.tsx` | SchemaRenderer form (new) |
346+
| Record Edit | `apps/web/src/pages/apps/record-edit.tsx` | SchemaRenderer form (edit) |
347+
| Error Boundary | `apps/web/src/components/ui/query-error-boundary.tsx` | TanStack Query error handling |
348+
| Recent Items | `apps/web/src/hooks/use-recent-items.ts` | Navigation history tracking |
339349

340350
---
341351

apps/web/src/App.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ const ObjectUIDemoPage = lazy(() => import('./pages/settings/objectui-demo'));
3737
const BusinessAppPage = lazy(() => import('./pages/apps/app'));
3838
const ObjectListPage = lazy(() => import('./pages/apps/object-list'));
3939
const ObjectRecordPage = lazy(() => import('./pages/apps/object-record'));
40+
const RecordCreatePage = lazy(() => import('./pages/apps/record-create'));
41+
const RecordEditPage = lazy(() => import('./pages/apps/record-edit'));
4042

4143
export function App() {
4244
const fallback = (
@@ -91,7 +93,9 @@ export function App() {
9193
<Route path="/apps/:appId" element={<AppLayout />}>
9294
<Route index element={<BusinessAppPage />} />
9395
<Route path=":objectName" element={<ObjectListPage />} />
96+
<Route path=":objectName/new" element={<RecordCreatePage />} />
9497
<Route path=":objectName/:recordId" element={<ObjectRecordPage />} />
98+
<Route path=":objectName/:recordId/edit" element={<RecordEditPage />} />
9599
</Route>
96100

97101
</Route>
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/**
2+
* Tests for FilterPanel component.
3+
*/
4+
import { describe, it, expect, vi } from 'vitest';
5+
import { render, screen, fireEvent } from '@testing-library/react';
6+
import { FilterPanel } from '@/components/objectui/FilterPanel';
7+
import type { ObjectDefinition } from '@/types/metadata';
8+
9+
const taskObjectDef: ObjectDefinition = {
10+
name: 'task',
11+
label: 'Task',
12+
pluralLabel: 'Tasks',
13+
fields: {
14+
id: { type: 'text', label: 'ID', readonly: true },
15+
title: { type: 'text', label: 'Title' },
16+
status: {
17+
type: 'select',
18+
label: 'Status',
19+
options: [
20+
{ label: 'To Do', value: 'todo' },
21+
{ label: 'In Progress', value: 'in_progress' },
22+
{ label: 'Done', value: 'done' },
23+
],
24+
},
25+
priority: {
26+
type: 'select',
27+
label: 'Priority',
28+
options: [
29+
{ label: 'Low', value: 'low' },
30+
{ label: 'High', value: 'high' },
31+
],
32+
},
33+
due_date: { type: 'datetime', label: 'Due Date' },
34+
created_at: { type: 'datetime', label: 'Created', readonly: true },
35+
},
36+
};
37+
38+
describe('FilterPanel', () => {
39+
it('renders search input', () => {
40+
const onFiltersChange = vi.fn();
41+
render(
42+
<FilterPanel
43+
objectDef={taskObjectDef}
44+
filters={[]}
45+
onFiltersChange={onFiltersChange}
46+
searchTerm=""
47+
onSearchChange={vi.fn()}
48+
/>,
49+
);
50+
expect(screen.getByLabelText('Search records')).toBeDefined();
51+
});
52+
53+
it('renders filter button', () => {
54+
render(
55+
<FilterPanel
56+
objectDef={taskObjectDef}
57+
filters={[]}
58+
onFiltersChange={vi.fn()}
59+
/>,
60+
);
61+
expect(screen.getByText('Filters')).toBeDefined();
62+
});
63+
64+
it('shows active filter count', () => {
65+
render(
66+
<FilterPanel
67+
objectDef={taskObjectDef}
68+
filters={[{ field: 'status', operator: 'equals', value: 'todo' }]}
69+
onFiltersChange={vi.fn()}
70+
/>,
71+
);
72+
expect(screen.getByText('1')).toBeDefined();
73+
});
74+
75+
it('displays active filter badges', () => {
76+
render(
77+
<FilterPanel
78+
objectDef={taskObjectDef}
79+
filters={[{ field: 'status', operator: 'equals', value: 'todo' }]}
80+
onFiltersChange={vi.fn()}
81+
/>,
82+
);
83+
expect(screen.getByText('Status:')).toBeDefined();
84+
expect(screen.getByText('todo')).toBeDefined();
85+
});
86+
87+
it('shows filter builder when expanded', () => {
88+
render(
89+
<FilterPanel
90+
objectDef={taskObjectDef}
91+
filters={[]}
92+
onFiltersChange={vi.fn()}
93+
/>,
94+
);
95+
fireEvent.click(screen.getByText('Filters'));
96+
expect(screen.getByLabelText('Filter field')).toBeDefined();
97+
});
98+
99+
it('calls onSearchChange when search input changes', () => {
100+
const onSearchChange = vi.fn();
101+
render(
102+
<FilterPanel
103+
objectDef={taskObjectDef}
104+
filters={[]}
105+
onFiltersChange={vi.fn()}
106+
searchTerm=""
107+
onSearchChange={onSearchChange}
108+
/>,
109+
);
110+
fireEvent.change(screen.getByLabelText('Search records'), {
111+
target: { value: 'test' },
112+
});
113+
expect(onSearchChange).toHaveBeenCalledWith('test');
114+
});
115+
});

0 commit comments

Comments
 (0)