Skip to content

Commit 743a6a5

Browse files
Copilothotlong
andauthored
Merge remote-tracking branch 'origin/main' into dependabot/npm_and_yarn/typescript-6.0.2
# Conflicts: # packages/core/src/protocols/DndProtocol.ts # packages/core/src/protocols/KeyboardProtocol.ts Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
2 parents d396d62 + 01cf1ca commit 743a6a5

38 files changed

+2185
-857
lines changed

CHANGELOG.md

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,67 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Changed
11+
12+
- **Merged ObjectManagerPage into MetadataManagerPage pipeline** (`@object-ui/console`): Removed the standalone `ObjectManagerPage` component. Object management is now fully handled by the generic `MetadataManagerPage` (list view) and `MetadataDetailPage` (detail view) pipeline. The object type config in `metadataTypeRegistry` uses `listComponent: ObjectManagerListAdapter` for the custom list UI and `pageSchemaFactory: buildObjectDetailPageSchema` for the detail page, eliminating redundant page code and centralizing all metadata management through a single architecture.
13+
14+
- **`listComponent` extension point on MetadataTypeConfig** (`@object-ui/console`): New optional `listComponent` field allows metadata types to inject a fully custom list component into `MetadataManagerPage`, replacing the default card/grid rendering. The page shell (header, back button, title) is still rendered by the generic manager. `MetadataListComponentProps` interface provides `config`, `basePath`, `metadataType`, and `isAdmin` to the custom component.
15+
16+
- **Routes unified to `/system/metadata/object`** (`@object-ui/console`): All entry points (sidebar, QuickActions, SystemHubPage hub cards) now point to `/system/metadata/object` instead of `/system/objects`. Legacy `/system/objects` routes redirect to the new path for backward compatibility.
17+
18+
### Removed
19+
20+
- **`ObjectManagerPage`** (`@object-ui/console`): Deleted `pages/system/ObjectManagerPage.tsx`. All object management functionality is now delivered through the `ObjectManagerListAdapter` + `MetadataManagerPage`/`MetadataDetailPage` pipeline.
21+
22+
- **`customRoute` on object type config** (`@object-ui/console`): The object metadata type no longer uses `customRoute: '/system/objects'`. It now routes through the standard metadata pipeline at `/system/metadata/object`.
23+
24+
### Fixed
25+
26+
- **Protocol bridges** (`@object-ui/core`): Updated `DndProtocol`, `KeyboardProtocol`, and `NotificationProtocol` to align with `@objectstack/spec` v4 type changes where `ariaLabel`, `label`, `title`, and `message` fields are now plain strings instead of i18n translation objects (`{ key, defaultValue }`).
27+
28+
- **CRM example** (`@object-ui/example-crm`): Converted all i18n label objects (`{ key, defaultValue }`) in `crm.app.ts` and `crm.dashboard.ts` to plain strings to match the updated `@objectstack/spec` v4 schema requirements.
29+
30+
- **Console app** (`@object-ui/console`): Fixed unused import warnings (`MetadataFormFieldDef`, `MetadataActionDef`, `toast`) and a `defaultValue` type mismatch (`unknown``string | undefined`) in `MetadataService.ts`.
31+
32+
### Changed
33+
34+
- **Object detail page migrated to PageSchema-driven rendering** (`@object-ui/console`): The object detail page (both at `/system/objects/:objectName` and `/system/metadata/object/:objectName`) is now rendered via `SchemaRenderer` using a `PageSchema` built by `buildObjectDetailPageSchema()`. Each section (properties, relationships, keys, data experience, data preview, field designer) is a self-contained SchemaNode widget registered in the ComponentRegistry. This replaces the monolithic `ObjectDetailView` component with a composable, metadata-driven architecture.
35+
36+
- **MetadataDetailPage unified PageSchema support** (`@object-ui/console`): `MetadataDetailPage` now supports three rendering modes in priority order: (1) PageSchema-driven via `pageSchemaFactory` in the registry config, (2) custom `detailComponent`, (3) default card layout. The `hasCustomPage` + `<Navigate>` redirect hack has been removed — all metadata detail pages are rendered directly by `MetadataDetailPage`.
37+
38+
- **MetadataTypeConfig refactored** (`@object-ui/console`): Replaced `hasCustomPage` boolean flag with the more expressive `pageSchemaFactory` function and kept `customRoute` for hub card linking. The `getGenericMetadataTypes()` helper now filters by `customRoute` instead of `hasCustomPage`.
39+
40+
- **SchemaErrorBoundary for detail pages** (`@object-ui/console`): Added `SchemaErrorBoundary` class component to both `MetadataDetailPage` and `ObjectManagerPage` to gracefully catch and display rendering errors when a PageSchema or its widgets fail to render.
41+
1042
### Added
1143

44+
- **Object detail schema widgets** (`@object-ui/console`): Six self-contained SchemaNode widget components for the object detail page, registered in ComponentRegistry: `object-properties`, `object-relationships`, `object-keys`, `object-data-experience`, `object-data-preview`, `object-field-designer`. Each widget resolves its data from React context (`useMetadata`, `useMetadataService`) making them fully composable via PageSchema.
45+
46+
- **Object detail PageSchema factory** (`@object-ui/console`): `buildObjectDetailPageSchema(objectName, item?)` generates a `PageSchema` for object detail pages. The schema defines the page structure as an array of widget nodes, enabling server-driven UI customization of the object detail page layout.
47+
48+
- **`pageSchemaFactory` on MetadataTypeConfig** (`@object-ui/console`): New optional factory function on the registry config that generates a `PageSchema` for detail page rendering via `SchemaRenderer`. When defined, `MetadataDetailPage` uses schema rendering instead of the default card layout.
49+
50+
- **Grid list mode for MetadataManagerPage** (`@object-ui/console`): MetadataManagerPage now supports `listMode: 'grid' | 'table'` configuration via the metadata type registry. When set, items are rendered in a professional table layout with column headers, sortable rows, and inline action buttons — matching the Power Apps table listing UX. The `report` type is configured to use grid mode by default.
51+
52+
- **Enhanced Object Detail View (Power Apps alignment)** (`@object-ui/console`): ObjectDetailView now includes dedicated sections beyond the existing Object Properties and Fields:
53+
- **Relationships section**: Displays all relationships with type badges (one-to-many, many-to-one), related object names, and foreign key info. Shows empty state message when no relationships are defined.
54+
- **Keys section**: Automatically extracts and displays primary keys, unique keys, and external ID fields from the object's field metadata.
55+
- **Data Experience section**: Placeholder cards for Forms, Views, and Dashboards design capabilities — preparing the UI structure for future implementation.
56+
- **Inline data preview placeholder**: Dedicated "Data Preview" section with placeholder UI for sample data grid (Power Apps parity).
57+
- **System field hints**: Visual indicator warning that system fields (id, createdAt, updatedAt) are read-only and cannot be edited.
58+
59+
- **Reusable MetadataGrid component** (`@object-ui/console`): Extracted the grid/table rendering logic from MetadataManagerPage into a standalone `MetadataGrid` component (`components/MetadataGrid.tsx`). Supports configurable columns, action buttons, row click handlers, and delete confirmation state. Can be reused by any metadata type list page.
60+
61+
- **MetadataDetailPage unified schema rendering** (`@object-ui/console`): MetadataDetailPage now renders object detail pages via PageSchema (using `pageSchemaFactory`) instead of redirecting to `/system/objects/:name`. The redirect-based approach (`hasCustomPage` + `<Navigate>`) has been replaced with direct schema rendering.
62+
63+
- **MetadataProvider dynamic type access** (`@object-ui/console`): Added `getItemsByType(type)` method to `MetadataContextValue`, allowing pages to access cached metadata items for any known type without hardcoding property names.
64+
65+
- **Number and boolean field types in MetadataFormDialog** (`@object-ui/console`): Extended `MetadataFormFieldDef.type` to support `'number'` (renders HTML number input) and `'boolean'` (renders a Shadcn Switch toggle with Yes/No label). All existing field types (`text`, `textarea`, `select`) continue to work unchanged.
66+
67+
- **Shared metadata converters utility** (`@object-ui/console`): Extracted `toObjectDefinition()` and `toFieldDefinition()` from ObjectManagerPage into a shared `utils/metadataConverters.ts` module. These functions convert raw ObjectStack API metadata shapes to the UI types (`ObjectDefinition`, `DesignerFieldDefinition`), and can now be reused across pages.
68+
69+
- **Unified icon resolver** (`@object-ui/console`): Consolidated three duplicated `ICON_MAP` + `resolveIcon()` implementations (in MetadataManagerPage, MetadataDetailPage, SystemHubPage) into a single `getIcon()` utility from `utils/getIcon.ts`, which dynamically resolves any Lucide icon by kebab-case or PascalCase name.
70+
1271
- **Metadata Create & Edit via MetadataFormDialog** (`@object-ui/console`): New generic `MetadataFormDialog` component (`components/MetadataFormDialog.tsx`) provides a registry-driven create/edit dialog for any metadata type. Form fields are determined by the `formFields` configuration in the metadata type registry, with fallback defaults (`name`, `label`, `description`). Supports required validation, `disabledOnEdit` for immutable keys (e.g. `name`), textarea and select field types, and loading state during submission.
1372

1473
- **MetadataManagerPage CRUD enhancements** (`@object-ui/console`): Extended the generic MetadataManagerPage with "New" button in the header (opens create dialog), per-item edit buttons (opens pre-filled edit dialog), and click-to-navigate to the detail page. All mutations use `MetadataService.saveMetadataItem()` with toast feedback, loading state, and automatic list refresh.

ROADMAP.md

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -908,7 +908,7 @@ ObjectUI is a universal Server-Driven UI (SDUI) engine built on React + Tailwind
908908

909909
### P1.16 Object Manager & Field Designer ✅
910910

911-
> **Status:** Complete — `ObjectManager` and `FieldDesigner` components shipped in `@object-ui/plugin-designer`.
911+
> **Status:** Complete — `ObjectManager` and `FieldDesigner` components shipped in `@object-ui/plugin-designer`. Object Detail View enhanced with Power Apps-style sections.
912912
913913
Enterprise-grade visual designers for managing object definitions and configuring fields. Supports the full metadata platform workflow: define objects, configure fields with advanced properties, and maintain relationships.
914914

@@ -924,6 +924,35 @@ Enterprise-grade visual designers for managing object definitions and configurin
924924
- [x] Confirm dialog for destructive actions
925925
- [x] 18 unit tests
926926

927+
**Object Detail View (Power Apps alignment):**
928+
- [x] Dedicated Relationships section with type badges, foreign key info, and empty state
929+
- [x] Keys section auto-extracting primary keys, unique keys, and external IDs from field metadata
930+
- [x] Data Experience placeholder section (Forms, Views, Dashboards) — UI structure ready for future implementation
931+
- [x] Inline data preview placeholder section (Power Apps sample data grid parity)
932+
- [x] System field non-editable visual hints
933+
- [x] Enhanced object properties card with separated concern sections
934+
935+
**Metadata Manager Grid Mode:**
936+
- [x] `listMode: 'grid' | 'table' | 'card'` configuration on MetadataTypeConfig
937+
- [x] Professional table rendering with column headers and action buttons in grid mode
938+
- [x] Report type configured with grid mode by default
939+
- [x] Reusable `MetadataGrid` component extracted for cross-page reuse
940+
941+
**MetadataFormDialog Enhancements:**
942+
- [x] `number` field type — renders HTML number input
943+
- [x] `boolean` field type — renders Shadcn Switch toggle with Yes/No label
944+
945+
**MetadataDetailPage & Provider Enhancements:**
946+
- [x] Auto-redirect for custom page types (object → `/system/objects/:name`) — removed; object now uses metadata pipeline
947+
- [x] `getItemsByType(type)` method on MetadataProvider for dynamic registry access
948+
- [x] Object type merged into MetadataManagerPage pipeline — `ObjectManagerPage` removed, replaced by `ObjectManagerListAdapter` via `listComponent` extension point
949+
- [x] `listComponent` extension point on MetadataTypeConfig for injecting custom list UIs
950+
- [x] All entry points (sidebar, QuickActions, hub cards) unified to `/system/metadata/object`
951+
952+
**Technical Debt Cleanup:**
953+
- [x] Unified icon resolver — consolidated 3 duplicated ICON_MAP/resolveIcon into shared `getIcon` utility
954+
- [x] Extracted `toObjectDefinition`/`toFieldDefinition` to shared `utils/metadataConverters.ts`
955+
927956
**Field Designer (`FieldDesigner`):**
928957
- [x] CRUD operations on field definitions with 27 supported field types
929958
- [x] Advanced field properties: uniqueness, default values, options/picklists, read-only, hidden, validation rules, external ID, history tracking, indexed

apps/console/src/App.tsx

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { BrowserRouter, Routes, Route, Navigate, useNavigate, useLocation } from 'react-router-dom';
1+
import { BrowserRouter, Routes, Route, Navigate, useNavigate, useLocation, useParams } from 'react-router-dom';
22
import { useState, useEffect, useCallback, lazy, Suspense, useMemo, type ReactNode } from 'react';
33
import { ModalForm } from '@object-ui/plugin-form';
44
import { Empty, EmptyTitle, EmptyDescription, Button } from '@object-ui/components';
@@ -47,7 +47,6 @@ const ForgotPasswordPage = lazy(() => import('./pages/ForgotPasswordPage').then(
4747
// System Admin Pages (lazy — rarely accessed)
4848
const SystemHubPage = lazy(() => import('./pages/system/SystemHubPage').then(m => ({ default: m.SystemHubPage })));
4949
const AppManagementPage = lazy(() => import('./pages/system/AppManagementPage').then(m => ({ default: m.AppManagementPage })));
50-
const ObjectManagerPage = lazy(() => import('./pages/system/ObjectManagerPage').then(m => ({ default: m.ObjectManagerPage })));
5150
const MetadataManagerPage = lazy(() => import('./pages/system/MetadataManagerPage').then(m => ({ default: m.MetadataManagerPage })));
5251
const MetadataDetailPage = lazy(() => import('./pages/system/MetadataDetailPage').then(m => ({ default: m.MetadataDetailPage })));
5352
const UserManagementPage = lazy(() => import('./pages/system/UserManagementPage').then(m => ({ default: m.UserManagementPage })));
@@ -61,7 +60,6 @@ const ProfilePage = lazy(() => import('./pages/system/ProfilePage').then(m => ({
6160
const HomePage = lazy(() => import('./pages/home/HomePage').then(m => ({ default: m.HomePage })));
6261
const HomeLayout = lazy(() => import('./pages/home/HomeLayout').then(m => ({ default: m.HomeLayout })));
6362

64-
import { useParams } from 'react-router-dom';
6563
import { ThemeProvider } from './components/theme-provider';
6664
import { ConsoleToaster } from './components/ConsoleToaster';
6765

@@ -294,8 +292,8 @@ export function AppContent() {
294292
<Route path="create-app" element={<CreateAppPage />} />
295293
<Route path="system" element={<SystemHubPage />} />
296294
<Route path="system/apps" element={<AppManagementPage />} />
297-
<Route path="system/objects" element={<ObjectManagerPage />} />
298-
<Route path="system/objects/:objectName" element={<ObjectManagerPage />} />
295+
<Route path="system/objects" element={<Navigate to="/system/metadata/object" replace />} />
296+
<Route path="system/objects/:objectName" element={<ObjectRedirect />} />
299297
<Route path="system/users" element={<UserManagementPage />} />
300298
<Route path="system/organizations" element={<OrgManagementPage />} />
301299
<Route path="system/roles" element={<RoleManagementPage />} />
@@ -390,8 +388,8 @@ export function AppContent() {
390388
{/* System Administration Routes */}
391389
<Route path="system" element={<SystemHubPage />} />
392390
<Route path="system/apps" element={<AppManagementPage />} />
393-
<Route path="system/objects" element={<ObjectManagerPage />} />
394-
<Route path="system/objects/:objectName" element={<ObjectManagerPage />} />
391+
<Route path="system/objects" element={<Navigate to="/system/metadata/object" replace />} />
392+
<Route path="system/objects/:objectName" element={<ObjectRedirect />} />
395393
<Route path="system/users" element={<UserManagementPage />} />
396394
<Route path="system/organizations" element={<OrgManagementPage />} />
397395
<Route path="system/roles" element={<RoleManagementPage />} />
@@ -476,6 +474,19 @@ function RootRedirect() {
476474
return <Navigate to="/home" replace />;
477475
}
478476

477+
/**
478+
* Redirect helper for legacy `/system/objects/:objectName` routes.
479+
* Preserves the `objectName` URL param so that
480+
* `/system/objects/account` → `/system/metadata/object/account`.
481+
*/
482+
function ObjectRedirect() {
483+
const { objectName } = useParams<{ objectName?: string }>();
484+
const target = objectName
485+
? `/system/metadata/object/${objectName}`
486+
: '/system/metadata/object';
487+
return <Navigate to={target} replace />;
488+
}
489+
479490
/**
480491
* SystemRoutes — Top-level system admin routes accessible without any app context.
481492
* Provides a minimal layout with system navigation sidebar.
@@ -486,8 +497,8 @@ function SystemRoutes() {
486497
<Routes>
487498
<Route path="/" element={<SystemHubPage />} />
488499
<Route path="apps" element={<AppManagementPage />} />
489-
<Route path="objects" element={<ObjectManagerPage />} />
490-
<Route path="objects/:objectName" element={<ObjectManagerPage />} />
500+
<Route path="objects" element={<Navigate to="/system/metadata/object" replace />} />
501+
<Route path="objects/:objectName" element={<ObjectRedirect />} />
491502
<Route path="users" element={<UserManagementPage />} />
492503
<Route path="organizations" element={<OrgManagementPage />} />
493504
<Route path="roles" element={<RoleManagementPage />} />

0 commit comments

Comments
 (0)