From 190797c3add2f713da8d6b0d43ba816b9eb82a46 Mon Sep 17 00:00:00 2001
From: "anthropic-code-agent[bot]" <242468646+Claude@users.noreply.github.com>
Date: Wed, 1 Apr 2026 09:19:52 +0000
Subject: [PATCH 1/3] Initial plan
From 48b9f264b3559e8750163dc089d7f7c51fcf9777 Mon Sep 17 00:00:00 2001
From: "anthropic-code-agent[bot]" <242468646+Claude@users.noreply.github.com>
Date: Wed, 1 Apr 2026 09:25:37 +0000
Subject: [PATCH 2/3] Implement unified Home Dashboard with app cards, quick
actions, and recent items
Agent-Logs-Url: https://github.com/objectstack-ai/objectui/sessions/a9fae3f9-cf33-49ec-8ac5-4a3590b476ab
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
---
apps/console/src/App.tsx | 55 +++----
apps/console/src/pages/home/AppCard.tsx | 99 ++++++++++++
apps/console/src/pages/home/HomePage.tsx | 153 +++++++++++++++++++
apps/console/src/pages/home/QuickActions.tsx | 88 +++++++++++
apps/console/src/pages/home/RecentApps.tsx | 61 ++++++++
apps/console/src/pages/home/StarredApps.tsx | 61 ++++++++
apps/console/src/pages/home/index.ts | 11 ++
apps/console/src/utils/getIcon.ts | 23 +++
8 files changed, 515 insertions(+), 36 deletions(-)
create mode 100644 apps/console/src/pages/home/AppCard.tsx
create mode 100644 apps/console/src/pages/home/HomePage.tsx
create mode 100644 apps/console/src/pages/home/QuickActions.tsx
create mode 100644 apps/console/src/pages/home/RecentApps.tsx
create mode 100644 apps/console/src/pages/home/StarredApps.tsx
create mode 100644 apps/console/src/pages/home/index.ts
create mode 100644 apps/console/src/utils/getIcon.ts
diff --git a/apps/console/src/App.tsx b/apps/console/src/App.tsx
index e317f38d2..88c6396ca 100644
--- a/apps/console/src/App.tsx
+++ b/apps/console/src/App.tsx
@@ -55,6 +55,9 @@ const PermissionManagementPage = lazy(() => import('./pages/system/PermissionMan
const AuditLogPage = lazy(() => import('./pages/system/AuditLogPage').then(m => ({ default: m.AuditLogPage })));
const ProfilePage = lazy(() => import('./pages/system/ProfilePage').then(m => ({ default: m.ProfilePage })));
+// Home Page (lazy โ landing page)
+const HomePage = lazy(() => import('./pages/home/HomePage').then(m => ({ default: m.HomePage })));
+
import { useParams } from 'react-router-dom';
import { ThemeProvider } from './components/theme-provider';
import { ConsoleToaster } from './components/ConsoleToaster';
@@ -456,44 +459,14 @@ function findFirstRoute(items: any[]): string {
return '';
}
-// Redirect root to default app
+// Redirect root to home page
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];
-
+ const { loading } = useMetadata();
+
if (loading) return ;
- if (defaultApp) {
- return ;
- }
- return (
-
-
- {error ? 'Failed to Load Configuration' : 'No Apps Configured'}
-
- {error
- ? 'There was an error loading the configuration. You can still create an app or access System Settings.'
- : 'No applications have been registered. Create your first app or configure your system.'}
-
-
- navigate('/create-app')}
- data-testid="create-first-app-btn"
- >
- Create Your First App
-
- navigate('/system')}
- data-testid="go-to-settings-btn"
- >
- System Settings
-
-
-
-
- );
+
+ // Always redirect to home page
+ return ;
}
/**
@@ -531,6 +504,16 @@ export function App() {
} />
} />
} />
+ {/* Home Dashboard โ unified workspace landing page */}
+ } loadingFallback={ }>
+
+ }>
+
+
+
+
+ } />
{/* Top-level system routes โ accessible without any app */}
} loadingFallback={ }>
diff --git a/apps/console/src/pages/home/AppCard.tsx b/apps/console/src/pages/home/AppCard.tsx
new file mode 100644
index 000000000..742c9836a
--- /dev/null
+++ b/apps/console/src/pages/home/AppCard.tsx
@@ -0,0 +1,99 @@
+/**
+ * AppCard
+ *
+ * Display card for an application with icon, name, description, and favorite toggle.
+ *
+ * @module
+ */
+
+import { Star, StarOff } from 'lucide-react';
+import { Card, CardContent, Button } from '@object-ui/components';
+import { useObjectTranslation } from '@object-ui/i18n';
+import { resolveI18nLabel } from '../../utils';
+import { useFavorites } from '../../hooks/useFavorites';
+import { getIcon } from '../../utils/getIcon';
+import { cn } from '@object-ui/components';
+
+interface AppCardProps {
+ app: any;
+ onClick: () => void;
+ isFavorite: boolean;
+}
+
+export function AppCard({ app, onClick, isFavorite }: AppCardProps) {
+ const { t } = useObjectTranslation();
+ const { toggleFavorite } = useFavorites();
+
+ const Icon = getIcon(app.icon);
+ const label = resolveI18nLabel(app.label, t) || app.name;
+ const description = resolveI18nLabel(app.description, t);
+ const primaryColor = app.branding?.primaryColor;
+
+ const handleToggleFavorite = (e: React.MouseEvent) => {
+ e.stopPropagation();
+ toggleFavorite({
+ id: `app:${app.name}`,
+ label,
+ href: `/apps/${app.name}`,
+ type: 'object',
+ });
+ };
+
+ return (
+
+
+ {/* Favorite Button */}
+
+ {isFavorite ? (
+
+ ) : (
+
+ )}
+
+
+ {/* App Icon */}
+
+
+
+
+ {/* App Info */}
+
+
{label}
+ {description && (
+
{description}
+ )}
+ {!description && (
+
+ {t('home.appCard.noDescription', { defaultValue: 'No description' })}
+
+ )}
+
+
+ {/* App Badge (if default) */}
+ {app.isDefault && (
+
+
+ {t('home.appCard.default', { defaultValue: 'Default' })}
+
+
+ )}
+
+
+ );
+}
diff --git a/apps/console/src/pages/home/HomePage.tsx b/apps/console/src/pages/home/HomePage.tsx
new file mode 100644
index 000000000..df7d22f73
--- /dev/null
+++ b/apps/console/src/pages/home/HomePage.tsx
@@ -0,0 +1,153 @@
+/**
+ * HomePage
+ *
+ * Unified Home Dashboard (Workspace) that displays all available applications,
+ * quick actions, recent items, and favorites. Inspired by Airtable/Notion home pages.
+ *
+ * Features:
+ * - Display all active applications as cards
+ * - Quick actions for creating apps, importing data, etc.
+ * - Recent apps section (from useRecentItems)
+ * - Starred/Favorite apps section (from useFavorites)
+ * - Empty state guidance for new users
+ * - Responsive grid layout
+ * - i18n support
+ *
+ * @module
+ */
+
+import { useNavigate } from 'react-router-dom';
+import { useMetadata } from '../../context/MetadataProvider';
+import { useRecentItems } from '../../hooks/useRecentItems';
+import { useFavorites } from '../../hooks/useFavorites';
+import { useObjectTranslation } from '@object-ui/i18n';
+import { resolveI18nLabel } from '../../utils';
+import { QuickActions } from './QuickActions';
+import { AppCard } from './AppCard';
+import { RecentApps } from './RecentApps';
+import { StarredApps } from './StarredApps';
+import { Empty, EmptyTitle, EmptyDescription, Button } from '@object-ui/components';
+import { Plus, Settings } from 'lucide-react';
+
+export function HomePage() {
+ const navigate = useNavigate();
+ const { t } = useObjectTranslation();
+ const { apps, loading } = useMetadata();
+ const { recentItems } = useRecentItems();
+ const { favorites } = useFavorites();
+
+ // Filter active apps
+ const activeApps = apps.filter((a: any) => a.active !== false);
+
+ // Get recent apps (only apps, not objects/dashboards)
+ const recentApps = recentItems
+ .filter(item => item.type === 'object' || item.type === 'dashboard' || item.type === 'page')
+ .slice(0, 6);
+
+ // Get starred apps
+ const starredApps = favorites
+ .filter(item => item.type === 'object' || item.type === 'dashboard' || item.type === 'page')
+ .slice(0, 8);
+
+ if (loading) {
+ return (
+
+ );
+ }
+
+ // Empty state - no apps configured
+ if (activeApps.length === 0) {
+ return (
+
+
+ Welcome to ObjectUI
+
+ Get started by creating your first application or configure your system settings.
+
+
+
navigate('/create-app')}
+ data-testid="create-first-app-btn"
+ >
+
+ Create Your First App
+
+
navigate('/system')}
+ data-testid="go-to-settings-btn"
+ >
+
+ System Settings
+
+
+
+
+ );
+ }
+
+ return (
+
+ {/* Header */}
+
+
+
+
+
+ {t('home.title', { defaultValue: 'Home' })}
+
+
+ {t('home.subtitle', { defaultValue: 'Your workspace dashboard' })}
+
+
+
+ navigate('/system')}
+ data-testid="home-settings-btn"
+ >
+
+ {t('common.settings', { defaultValue: 'Settings' })}
+
+
+
+
+
+
+ {/* Main Content */}
+
+ {/* Quick Actions */}
+
+
+ {/* Starred/Favorite Apps */}
+ {starredApps.length > 0 && (
+
+ )}
+
+ {/* Recent Apps */}
+ {recentApps.length > 0 && (
+
+ )}
+
+ {/* All Applications */}
+
+
+ {t('home.allApps', { defaultValue: 'All Applications' })}
+
+
+ {activeApps.map((app: any) => (
+
navigate(`/apps/${app.name}`)}
+ isFavorite={favorites.some(f => f.id === `app:${app.name}`)}
+ />
+ ))}
+
+
+
+
+ );
+}
diff --git a/apps/console/src/pages/home/QuickActions.tsx b/apps/console/src/pages/home/QuickActions.tsx
new file mode 100644
index 000000000..06b05f84f
--- /dev/null
+++ b/apps/console/src/pages/home/QuickActions.tsx
@@ -0,0 +1,88 @@
+/**
+ * QuickActions
+ *
+ * Quick action cards for common tasks like creating apps, importing data,
+ * accessing system settings, etc.
+ *
+ * @module
+ */
+
+import { useNavigate } from 'react-router-dom';
+import { useObjectTranslation } from '@object-ui/i18n';
+import { Card, CardContent } from '@object-ui/components';
+import { Plus, Upload, Settings, Database, FileText } from 'lucide-react';
+import { cn } from '@object-ui/components';
+
+interface QuickAction {
+ id: string;
+ label: string;
+ description: string;
+ icon: React.ElementType;
+ href: string;
+ color: string;
+}
+
+export function QuickActions() {
+ const navigate = useNavigate();
+ const { t } = useObjectTranslation();
+
+ const actions: QuickAction[] = [
+ {
+ id: 'create-app',
+ label: t('home.quickActions.createApp', { defaultValue: 'Create App' }),
+ description: t('home.quickActions.createAppDesc', { defaultValue: 'Start with a new application' }),
+ icon: Plus,
+ href: '/create-app',
+ color: 'text-blue-600 dark:text-blue-400',
+ },
+ {
+ id: 'manage-objects',
+ label: t('home.quickActions.manageObjects', { defaultValue: 'Manage Objects' }),
+ description: t('home.quickActions.manageObjectsDesc', { defaultValue: 'Configure data models' }),
+ icon: Database,
+ href: '/system/objects',
+ color: 'text-purple-600 dark:text-purple-400',
+ },
+ {
+ id: 'system-settings',
+ label: t('home.quickActions.systemSettings', { defaultValue: 'System Settings' }),
+ description: t('home.quickActions.systemSettingsDesc', { defaultValue: 'Configure your workspace' }),
+ icon: Settings,
+ href: '/system',
+ color: 'text-gray-600 dark:text-gray-400',
+ },
+ ];
+
+ return (
+
+
+ {t('home.quickActions.title', { defaultValue: 'Quick Actions' })}
+
+
+ {actions.map((action) => {
+ const Icon = action.icon;
+ return (
+
navigate(action.href)}
+ data-testid={`quick-action-${action.id}`}
+ >
+
+
+
+
+
+
+
{action.label}
+
{action.description}
+
+
+
+
+ );
+ })}
+
+
+ );
+}
diff --git a/apps/console/src/pages/home/RecentApps.tsx b/apps/console/src/pages/home/RecentApps.tsx
new file mode 100644
index 000000000..1ab535fd2
--- /dev/null
+++ b/apps/console/src/pages/home/RecentApps.tsx
@@ -0,0 +1,61 @@
+/**
+ * RecentApps
+ *
+ * Display section for recently accessed items (objects, dashboards, pages).
+ *
+ * @module
+ */
+
+import { useNavigate } from 'react-router-dom';
+import { useObjectTranslation } from '@object-ui/i18n';
+import { Card, CardContent } from '@object-ui/components';
+import { Clock } from 'lucide-react';
+import { getIcon } from '../../utils/getIcon';
+import type { RecentItem } from '../../hooks/useRecentItems';
+
+interface RecentAppsProps {
+ items: RecentItem[];
+}
+
+export function RecentApps({ items }: RecentAppsProps) {
+ const navigate = useNavigate();
+ const { t } = useObjectTranslation();
+
+ if (items.length === 0) return null;
+
+ return (
+
+
+
+
+ {t('home.recentApps.title', { defaultValue: 'Recently Accessed' })}
+
+
+
+ {items.map((item) => {
+ const Icon = getIcon(item.type);
+ return (
+
navigate(item.href)}
+ data-testid={`recent-item-${item.id}`}
+ >
+
+
+
+
+
+
+
{item.label}
+
{item.type}
+
+
+
+
+ );
+ })}
+
+
+ );
+}
diff --git a/apps/console/src/pages/home/StarredApps.tsx b/apps/console/src/pages/home/StarredApps.tsx
new file mode 100644
index 000000000..98b6dea71
--- /dev/null
+++ b/apps/console/src/pages/home/StarredApps.tsx
@@ -0,0 +1,61 @@
+/**
+ * StarredApps
+ *
+ * Display section for starred/favorite items (objects, dashboards, pages).
+ *
+ * @module
+ */
+
+import { useNavigate } from 'react-router-dom';
+import { useObjectTranslation } from '@object-ui/i18n';
+import { Card, CardContent } from '@object-ui/components';
+import { Star } from 'lucide-react';
+import { getIcon } from '../../utils/getIcon';
+import type { FavoriteItem } from '../../hooks/useFavorites';
+
+interface StarredAppsProps {
+ items: FavoriteItem[];
+}
+
+export function StarredApps({ items }: StarredAppsProps) {
+ const navigate = useNavigate();
+ const { t } = useObjectTranslation();
+
+ if (items.length === 0) return null;
+
+ return (
+
+
+
+
+ {t('home.starredApps.title', { defaultValue: 'Starred' })}
+
+
+
+ {items.map((item) => {
+ const Icon = getIcon(item.type);
+ return (
+
navigate(item.href)}
+ data-testid={`starred-item-${item.id}`}
+ >
+
+
+
+
+
+
+
{item.label}
+
{item.type}
+
+
+
+
+ );
+ })}
+
+
+ );
+}
diff --git a/apps/console/src/pages/home/index.ts b/apps/console/src/pages/home/index.ts
new file mode 100644
index 000000000..edfe52d91
--- /dev/null
+++ b/apps/console/src/pages/home/index.ts
@@ -0,0 +1,11 @@
+/**
+ * Home Page Components
+ *
+ * Exports for the unified home dashboard components
+ */
+
+export { HomePage } from './HomePage';
+export { QuickActions } from './QuickActions';
+export { AppCard } from './AppCard';
+export { RecentApps } from './RecentApps';
+export { StarredApps } from './StarredApps';
diff --git a/apps/console/src/utils/getIcon.ts b/apps/console/src/utils/getIcon.ts
new file mode 100644
index 000000000..03047ed96
--- /dev/null
+++ b/apps/console/src/utils/getIcon.ts
@@ -0,0 +1,23 @@
+/**
+ * Icon utilities
+ *
+ * Helpers for resolving Lucide icons by name.
+ */
+
+import * as LucideIcons from 'lucide-react';
+import { Database } from 'lucide-react';
+
+/**
+ * Resolve a Lucide icon by name (kebab-case or PascalCase)
+ * Falls back to Database icon if not found
+ */
+export function getIcon(name?: string): React.ElementType {
+ if (!name) return Database;
+ if ((LucideIcons as any)[name]) return (LucideIcons as any)[name];
+ const pascal = name
+ .split('-')
+ .map(p => p.charAt(0).toUpperCase() + p.slice(1))
+ .join('');
+ if ((LucideIcons as any)[pascal]) return (LucideIcons as any)[pascal];
+ return Database;
+}
From 72087d1c4095338b20547c698f13e2aeec6a78a7 Mon Sep 17 00:00:00 2001
From: "anthropic-code-agent[bot]" <242468646+Claude@users.noreply.github.com>
Date: Wed, 1 Apr 2026 09:26:30 +0000
Subject: [PATCH 3/3] Update ROADMAP.md with Unified Home Dashboard feature
(P1.7.1)
Agent-Logs-Url: https://github.com/objectstack-ai/objectui/sessions/a9fae3f9-cf33-49ec-8ac5-4a3590b476ab
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
---
ROADMAP.md | 22 +++++++++++++++++++---
1 file changed, 19 insertions(+), 3 deletions(-)
diff --git a/ROADMAP.md b/ROADMAP.md
index db1077047..fdc5d361f 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -1,11 +1,11 @@
# ObjectUI Development Roadmap
-> **Last Updated:** March 23, 2026
+> **Last Updated:** April 1, 2026
> **Current Version:** v0.5.x
> **Spec Version:** @objectstack/spec v3.3.0
> **Client Version:** @objectstack/client v3.3.0
> **Target UX Benchmark:** ๐ฏ Airtable parity
-> **Current Priority:** AppShell Navigation ยท Designer Interaction ยท **View Config Live Preview Sync โ
** ยท Dashboard Config Panel ยท Airtable UX Polish ยท **Flow Designer โ
** ยท **App Creation & Editing Flow โ
** ยท **System Settings & App Management โ
** ยท **Right-Side Visual Editor Drawer โ
** ยท **Object Manager & Field Designer โ
** ยท **AI SDUI Chatbot (service-ai + vercel/ai) โ
**
+> **Current Priority:** AppShell Navigation ยท Designer Interaction ยท **View Config Live Preview Sync โ
** ยท Dashboard Config Panel ยท Airtable UX Polish ยท **Flow Designer โ
** ยท **App Creation & Editing Flow โ
** ยท **System Settings & App Management โ
** ยท **Right-Side Visual Editor Drawer โ
** ยท **Object Manager & Field Designer โ
** ยท **AI SDUI Chatbot (service-ai + vercel/ai) โ
** ยท **Unified Home Dashboard โ
**
---
@@ -13,7 +13,7 @@
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, 6,700+ tests, 80 Storybook stories, 43/43 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, Console through Phase 20 (L3), **AppShell Navigation Renderer** (P0.1), **Flow Designer** (P2.4), **Feed/Chatter UI** (P1.5), **App Creation & Editing Flow** (P1.11), **System Settings & App Management** (P1.12), **Page/Dashboard Editor Console Integration** (P1.11), **Right-Side Visual Editor Drawer** (P1.11), and **Console Engine Schema Integration** (P1.14) โ all โ
complete. **ViewDesigner** has been removed โ its capabilities (drag-to-reorder, undo/redo) are now provided by the ViewConfigPanel (right-side config panel).
+**Where We Are:** Foundation is **solid and shipping** โ 35 packages, 99+ components, 6,700+ tests, 80 Storybook stories, 43/43 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, Console through Phase 20 (L3), **AppShell Navigation Renderer** (P0.1), **Flow Designer** (P2.4), **Feed/Chatter UI** (P1.5), **App Creation & Editing Flow** (P1.11), **System Settings & App Management** (P1.12), **Page/Dashboard Editor Console Integration** (P1.11), **Right-Side Visual Editor Drawer** (P1.11), **Console Engine Schema Integration** (P1.14), and **Unified Home Dashboard** (P1.7.1) โ all โ
complete. **ViewDesigner** has been removed โ its capabilities (drag-to-reorder, undo/redo) are now provided by the ViewConfigPanel (right-side config panel).
**What Remains:** The gap to **Airtable-level UX** is primarily in:
1. ~~**AppShell** โ No dynamic navigation renderer from spec JSON (last P0 blocker)~~ โ
Complete
@@ -222,6 +222,22 @@ ObjectUI is a universal Server-Driven UI (SDUI) engine built on React + Tailwind
- โ
`useNavigationOverlay` hook delegates `new_window` to `onNavigate` when available for app-specific URL control
- โ
plugin-view `handleRowClick` supports `split` and `popover` branches
+### P1.7.1 Console โ Unified Home Dashboard (Workspace) โ
+
+- [x] **HomePage component** โ Unified landing page displaying all available applications
+- [x] **Route integration** โ `/home` route added with proper authentication guards
+- [x] **App cards grid** โ Responsive grid layout showing all active apps with icons, descriptions, and branding colors
+- [x] **QuickActions section** โ Quick access cards for creating apps, managing objects, and system settings
+- [x] **Recent items** โ Display recently accessed objects, dashboards, and pages using `useRecentItems` hook
+- [x] **Starred items** โ Display user-favorited items using `useFavorites` hook with star/unstar toggle
+- [x] **Empty state** โ Helpful guidance for new users with "Create First App" and "System Settings" CTAs
+- [x] **i18n support** โ All labels support internationalization via `useObjectTranslation`
+- [x] **RootRedirect update** โ Root path (`/`) now redirects to `/home` instead of first app
+- [x] **Responsive design** โ Mobile-friendly grid layouts that adapt to screen size
+- [x] **Airtable/Notion UX pattern** โ Inspired by industry-leading workspace home pages
+
+**Impact:** Users now have a unified workspace dashboard that provides overview of all applications, quick actions, and recent activity. This eliminates the previous behavior of auto-redirecting to the first app, giving users better control and visibility.
+
### P1.8 Console โ View Config Panel (Phase 20)
- [x] Inline ViewConfigPanel for all view types (Airtable-style right sidebar)