Skip to content

Commit bc97040

Browse files
authored
Merge pull request #761 from objectstack-ai/copilot/integrate-app-creation-wizard
2 parents b7c62d8 + de674d1 commit bc97040

File tree

19 files changed

+744
-3
lines changed

19 files changed

+744
-3
lines changed

ROADMAP.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,19 @@ ObjectUI is a universal Server-Driven UI (SDUI) engine built on React + Tailwind
482482
**ComponentRegistry:**
483483
- [x] Registered: `app-creation-wizard`, `navigation-designer`, `dashboard-editor`, `page-canvas-editor`, `object-view-configurator`
484484

485+
**Console Integration:**
486+
- [x] `CreateAppPage` — renders `AppCreationWizard` with `useMetadata()` objects, `onComplete`/`onCancel`/`onSaveDraft` callbacks
487+
- [x] `EditAppPage` — reuses wizard with `initialDraft` from existing app config
488+
- [x] Routes: `/apps/:appName/create-app`, `/apps/:appName/edit-app/:editAppName`
489+
- [x] AppSidebar "Add App" button → navigates to `/create-app`
490+
- [x] AppSidebar "Edit App" button → navigates to `/edit-app/:appName`
491+
- [x] CommandPalette "Create New App" command (⌘+K → Actions group)
492+
- [x] Empty state CTA "Create Your First App" when no apps configured
493+
- [x] `wizardDraftToAppSchema()` conversion on completion
494+
- [x] Draft persistence to localStorage with auto-clear on success
495+
- [x] `createApp` i18n key added to all 10 locales
496+
- [x] 11 console integration tests (routes, wizard callbacks, draft persistence, CommandPalette)
497+
485498
---
486499

487500
## 🧩 P2 — Polish & Advanced Features

apps/console/src/App.tsx

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ const ReportView = lazy(() => import('./components/ReportView').then(m => ({ def
3030
const ViewDesignerPage = lazy(() => import('./components/ViewDesignerPage').then(m => ({ default: m.ViewDesignerPage })));
3131
const SearchResultsPage = lazy(() => import('./components/SearchResultsPage').then(m => ({ default: m.SearchResultsPage })));
3232

33+
// App Creation / Edit Pages (lazy — only needed during app management)
34+
const CreateAppPage = lazy(() => import('./pages/CreateAppPage').then(m => ({ default: m.CreateAppPage })));
35+
const EditAppPage = lazy(() => import('./pages/EditAppPage').then(m => ({ default: m.EditAppPage })));
36+
3337
// Auth Pages (lazy — only needed before login)
3438
const LoginPage = lazy(() => import('./pages/LoginPage').then(m => ({ default: m.LoginPage })));
3539
const RegisterPage = lazy(() => import('./pages/RegisterPage').then(m => ({ default: m.RegisterPage })));
@@ -246,14 +250,37 @@ export function AppContent() {
246250
);
247251

248252
if (!dataSource || metadataLoading) return <LoadingScreen />;
249-
if (!activeApp) return (
253+
254+
// Allow create-app route even when no active app exists
255+
const isCreateAppRoute = location.pathname.endsWith('/create-app');
256+
257+
if (!activeApp && !isCreateAppRoute) return (
250258
<div className="h-screen flex items-center justify-center">
251259
<Empty>
252260
<EmptyTitle>No Apps Configured</EmptyTitle>
261+
<EmptyDescription>No applications have been registered.</EmptyDescription>
262+
<button
263+
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"
264+
onClick={() => navigate('create-app')}
265+
data-testid="create-first-app-btn"
266+
>
267+
Create Your First App
268+
</button>
253269
</Empty>
254270
</div>
255271
);
256272

273+
// When on create-app without an active app, render a minimal layout with just the wizard
274+
if (!activeApp && isCreateAppRoute) {
275+
return (
276+
<Suspense fallback={<LoadingScreen />}>
277+
<Routes>
278+
<Route path="create-app" element={<CreateAppPage />} />
279+
</Routes>
280+
</Suspense>
281+
);
282+
}
283+
257284
// Expression context for dynamic visibility/disabled/hidden expressions
258285
const expressionUser = user
259286
? { name: user.name, email: user.email, role: user.role ?? 'user' }
@@ -331,6 +358,10 @@ export function AppContent() {
331358
<SearchResultsPage />
332359
} />
333360

361+
{/* App Creation & Editing */}
362+
<Route path="create-app" element={<CreateAppPage />} />
363+
<Route path="edit-app/:editAppName" element={<EditAppPage />} />
364+
334365
{/* System Administration Routes */}
335366
<Route path="system/users" element={<UserManagementPage />} />
336367
<Route path="system/organizations" element={<OrgManagementPage />} />
@@ -402,6 +433,7 @@ function findFirstRoute(items: any[]): string {
402433
// Redirect root to default app
403434
function RootRedirect() {
404435
const { apps, loading, error } = useMetadata();
436+
const navigate = useNavigate();
405437
const activeApps = apps.filter((a: any) => a.active !== false);
406438
const defaultApp = activeApps.find((a: any) => a.isDefault === true) || activeApps[0];
407439

@@ -416,6 +448,15 @@ function RootRedirect() {
416448
<EmptyDescription>
417449
{error ? error.message : 'No applications have been registered. Check your ObjectStack configuration.'}
418450
</EmptyDescription>
451+
{!error && (
452+
<button
453+
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"
454+
onClick={() => navigate('/apps/_new/create-app')}
455+
data-testid="create-first-app-btn"
456+
>
457+
Create Your First App
458+
</button>
459+
)}
419460
</Empty>
420461
</div>
421462
);

0 commit comments

Comments
 (0)