Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,19 @@ ObjectUI is a universal Server-Driven UI (SDUI) engine built on React + Tailwind
**ComponentRegistry:**
- [x] Registered: `app-creation-wizard`, `navigation-designer`, `dashboard-editor`, `page-canvas-editor`, `object-view-configurator`

**Console Integration:**
- [x] `CreateAppPage` — renders `AppCreationWizard` with `useMetadata()` objects, `onComplete`/`onCancel`/`onSaveDraft` callbacks
- [x] `EditAppPage` — reuses wizard with `initialDraft` from existing app config
- [x] Routes: `/apps/:appName/create-app`, `/apps/:appName/edit-app/:editAppName`
- [x] AppSidebar "Add App" button → navigates to `/create-app`
- [x] AppSidebar "Edit App" button → navigates to `/edit-app/:appName`
- [x] CommandPalette "Create New App" command (⌘+K → Actions group)
Comment on lines +476 to +478
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These checklist items describe routes as /create-app and /edit-app/:appName, but the actual console routes are nested under /apps/:appName/… (e.g. /apps/:appName/create-app, /apps/:appName/edit-app/:editAppName). Update the roadmap entries to match the real paths so future readers don’t follow incorrect routes.

Suggested change
- [x] AppSidebar "Add App" button → navigates to `/create-app`
- [x] AppSidebar "Edit App" button → navigates to `/edit-app/:appName`
- [x] CommandPalette "Create New App" command (⌘+K → Actions group)
- [x] AppSidebar "Add App" button → navigates to `/apps/:appName/create-app`
- [x] AppSidebar "Edit App" button → navigates to `/apps/:appName/edit-app/:editAppName`
- [x] CommandPalette "Create New App" command (⌘+K → Actions group) → navigates to `/apps/:appName/create-app`

Copilot uses AI. Check for mistakes.
- [x] Empty state CTA "Create Your First App" when no apps configured
- [x] `wizardDraftToAppSchema()` conversion on completion
- [x] Draft persistence to localStorage with auto-clear on success
- [x] `createApp` i18n key added to all 10 locales
- [x] 11 console integration tests (routes, wizard callbacks, draft persistence, CommandPalette)

---

## 🧩 P2 — Polish & Advanced Features
Expand Down
43 changes: 42 additions & 1 deletion apps/console/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ const ReportView = lazy(() => import('./components/ReportView').then(m => ({ def
const ViewDesignerPage = lazy(() => import('./components/ViewDesignerPage').then(m => ({ default: m.ViewDesignerPage })));
const SearchResultsPage = lazy(() => import('./components/SearchResultsPage').then(m => ({ default: m.SearchResultsPage })));

// App Creation / Edit Pages (lazy — only needed during app management)
const CreateAppPage = lazy(() => import('./pages/CreateAppPage').then(m => ({ default: m.CreateAppPage })));
const EditAppPage = lazy(() => import('./pages/EditAppPage').then(m => ({ default: m.EditAppPage })));

// Auth Pages (lazy — only needed before login)
const LoginPage = lazy(() => import('./pages/LoginPage').then(m => ({ default: m.LoginPage })));
const RegisterPage = lazy(() => import('./pages/RegisterPage').then(m => ({ default: m.RegisterPage })));
Expand Down Expand Up @@ -246,14 +250,37 @@ export function AppContent() {
);

if (!dataSource || metadataLoading) return <LoadingScreen />;
if (!activeApp) return (

// Allow create-app route even when no active app exists
const isCreateAppRoute = location.pathname.endsWith('/create-app');
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isCreateAppRoute uses location.pathname.endsWith('/create-app'), which will fail for valid URLs with a trailing slash (e.g. /apps/x/create-app/) and incorrectly fall back to the "No Apps Configured" empty state. Use a route match (matchPath) or a more robust check like /\/create-app\/?$/ so the bypass guard works reliably.

Suggested change
const isCreateAppRoute = location.pathname.endsWith('/create-app');
const isCreateAppRoute = /\/create-app\/?$/.test(location.pathname);

Copilot uses AI. Check for mistakes.

if (!activeApp && !isCreateAppRoute) return (
<div className="h-screen flex items-center justify-center">
<Empty>
<EmptyTitle>No Apps Configured</EmptyTitle>
<EmptyDescription>No applications have been registered.</EmptyDescription>
<button
className="mt-4 inline-flex items-center rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90"
onClick={() => navigate('create-app')}
data-testid="create-first-app-btn"
>
Create Your First App
</button>
</Empty>
</div>
);

// When on create-app without an active app, render a minimal layout with just the wizard
if (!activeApp && isCreateAppRoute) {
return (
<Suspense fallback={<LoadingScreen />}>
<Routes>
<Route path="create-app" element={<CreateAppPage />} />
</Routes>
</Suspense>
);
Comment on lines +257 to +281
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New behavior introduced here (empty-state "Create Your First App" CTA + create-app route bypass when there’s no active app) isn’t covered by the added integration tests. Add a test case with apps: [] that asserts: (1) the empty state renders the CTA, (2) clicking it navigates to /apps/_new/create-app, and (3) the wizard renders in the minimal layout path.

Copilot uses AI. Check for mistakes.
}

// Expression context for dynamic visibility/disabled/hidden expressions
const expressionUser = user
? { name: user.name, email: user.email, role: user.role ?? 'user' }
Expand Down Expand Up @@ -331,6 +358,10 @@ export function AppContent() {
<SearchResultsPage />
} />

{/* App Creation & Editing */}
<Route path="create-app" element={<CreateAppPage />} />
<Route path="edit-app/:editAppName" element={<EditAppPage />} />

{/* System Administration Routes */}
<Route path="system/users" element={<UserManagementPage />} />
<Route path="system/organizations" element={<OrgManagementPage />} />
Expand Down Expand Up @@ -402,6 +433,7 @@ function findFirstRoute(items: any[]): string {
// Redirect root to default app
function RootRedirect() {
const { apps, loading, error } = useMetadata();
const navigate = useNavigate();
const activeApps = apps.filter((a: any) => a.active !== false);
const defaultApp = activeApps.find((a: any) => a.isDefault === true) || activeApps[0];

Expand All @@ -416,6 +448,15 @@ function RootRedirect() {
<EmptyDescription>
{error ? error.message : 'No applications have been registered. Check your ObjectStack configuration.'}
</EmptyDescription>
{!error && (
<button
className="mt-4 inline-flex items-center rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90"
onClick={() => navigate('/apps/_new/create-app')}
data-testid="create-first-app-btn"
>
Create Your First App
</button>
)}
</Empty>
</div>
);
Expand Down
Loading