Skip to content

Implement unified Home Dashboard for multi-app workspace overview#1169

Merged
hotlong merged 3 commits intomainfrom
claude/implement-home-dashboard
Apr 1, 2026
Merged

Implement unified Home Dashboard for multi-app workspace overview#1169
hotlong merged 3 commits intomainfrom
claude/implement-home-dashboard

Conversation

@Claude
Copy link
Copy Markdown
Contributor

@Claude Claude AI commented Apr 1, 2026

Replaces direct-to-app redirect behavior with a centralized workspace landing page displaying all applications, quick actions, and recent activity. Follows Airtable/Notion UX patterns.

Implementation

Route & Navigation

  • Added /home route with lazy-loaded HomePage component
  • Updated RootRedirect to navigate to /home instead of first available app
  • Integrated with existing AuthGuard, MetadataProvider, and ConnectedShell

Home Dashboard Components

  • HomePage — Main landing page with responsive grid layouts
  • QuickActions — Cards for Create App, Manage Objects, System Settings
  • AppCard — App display with icon, description, branding colors, favorite toggle
  • RecentApps — Last 6 accessed items via useRecentItems hook
  • StarredApps — User favorites via useFavorites hook
  • Empty state with "Create First App" and "System Settings" CTAs

Shared Utilities

  • Extracted getIcon() from CommandPalette to utils/getIcon.ts for Lucide icon resolution

Structure

// Root path now redirects to home
<Route path="/" element={
  <ConnectedShell>
    <RootRedirect /> {/* Navigate to="/home" */}
  </ConnectedShell>
} />

// Home dashboard route
<Route path="/home" element={
  <AuthGuard fallback={<Navigate to="/login" />}>
    <ConnectedShell>
      <HomePage />
    </ConnectedShell>
  </AuthGuard>
} />

User Flow Changes

Before: Login → auto-redirect to first/default app → no workspace overview
After: Login → /home workspace dashboard → user selects app

@vercel
Copy link
Copy Markdown

vercel bot commented Apr 1, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
objectui-demo Ready Ready Preview, Comment Apr 1, 2026 9:26am
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectui Ignored Ignored Apr 1, 2026 9:26am

Request Review

@Claude Claude AI changed the title [WIP] Add unified home dashboard for multi-application workspace Implement unified Home Dashboard for multi-app workspace overview Apr 1, 2026
@Claude Claude AI requested a review from hotlong April 1, 2026 09:27
@hotlong hotlong marked this pull request as ready for review April 1, 2026 09:28
Copilot AI review requested due to automatic review settings April 1, 2026 09:28
@hotlong hotlong merged commit 334afb1 into main Apr 1, 2026
5 checks passed
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Implements a unified Console “Home” dashboard (/home) as a workspace landing page, and changes the root route (/) to redirect to it instead of auto-opening the first app—aligning Console navigation with a multi-app workspace overview model.

Changes:

  • Added a new authenticated /home route that renders a new HomePage dashboard with Quick Actions, starred items, recent items, and an apps grid.
  • Updated RootRedirect to always navigate to /home.
  • Added a shared Lucide icon resolver utility (getIcon) and documented completion in ROADMAP.md.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 17 comments.

Show a summary per file
File Description
ROADMAP.md Updates “Last Updated” and documents the Unified Home Dashboard milestone/checklist.
apps/console/src/App.tsx Adds /home route and changes / redirect behavior to point to /home.
apps/console/src/utils/getIcon.ts Adds a shared Lucide icon resolver helper.
apps/console/src/pages/home/HomePage.tsx New workspace dashboard page composing quick actions, starred/recent sections, and app cards.
apps/console/src/pages/home/QuickActions.tsx New quick-action cards section for common workspace tasks.
apps/console/src/pages/home/AppCard.tsx New app card component with branding + favorite toggle.
apps/console/src/pages/home/RecentApps.tsx New “recently accessed” section component.
apps/console/src/pages/home/StarredApps.tsx New “starred” section component.
apps/console/src/pages/home/index.ts Barrel exports for the new home dashboard components.

import { useRecentItems } from '../../hooks/useRecentItems';
import { useFavorites } from '../../hooks/useFavorites';
import { useObjectTranslation } from '@object-ui/i18n';
import { resolveI18nLabel } from '../../utils';
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

resolveI18nLabel is imported but never used. With noUnusedLocals: true in apps/console/tsconfig.json, this will fail TypeScript compilation. Remove the import or use it where intended.

Suggested change
import { resolveI18nLabel } from '../../utils';

Copilot uses AI. Check for mistakes.
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';
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

Upload and FileText are imported but unused. Because this project enables noUnusedLocals, this will cause a build/typecheck failure. Remove the unused imports or use them in actions.

Suggested change
import { Plus, Upload, Settings, Database, FileText } from 'lucide-react';
import { Plus, Settings, Database } from 'lucide-react';

Copilot uses AI. Check for mistakes.
// Filter active apps
const activeApps = apps.filter((a: any) => a.active !== false);

// Get recent apps (only apps, not objects/dashboards)
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

The comment says "Get recent apps (only apps, not objects/dashboards)", but the filter includes object, dashboard, and page. This is misleading documentation and makes the later naming (recentApps) harder to follow.

Suggested change
// Get recent apps (only apps, not objects/dashboards)
// Get recent workspace items (objects, dashboards, and pages) for the RecentApps section

Copilot uses AI. Check for mistakes.
Comment on lines +52 to +56
if (loading) {
return (
<div className="min-h-screen flex items-center justify-center">
<div className="text-muted-foreground">Loading workspace...</div>
</div>
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

Several user-facing strings here are hard-coded (e.g., "Loading workspace...") instead of going through useObjectTranslation. This contradicts the PR's stated i18n support and will leave these strings untranslated.

Copilot uses AI. Check for mistakes.
id: `app:${app.name}`,
label,
href: `/apps/${app.name}`,
type: 'object',
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

toggleFavorite is storing an app favorite with type: 'object'. This causes the item to be mislabeled (and downstream UIs that render by type will treat an app as an object). Consider extending FavoriteItem['type'] to include 'app' (and updating filters/UI), or store a separate kind/entityType to avoid semantic mismatch.

Suggested change
type: 'object',
type: 'app',

Copilot uses AI. Check for mistakes.
Comment on lines +7 to +21
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];
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

import * as LucideIcons from 'lucide-react' pulls in the entire icon module and generally prevents tree-shaking, which can noticeably increase bundle size for the HomePage chunk. If icons are limited to a known set, consider importing only the needed icons and using a typed mapping instead of LucideIcons as any lookups.

Suggested change
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];
import { Database } from 'lucide-react';
const iconMap: Record<string, React.ElementType> = {
Database,
database: Database,
};
/**
* 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;
// Try direct lookup (supports both exact and kebab-case keys present in iconMap)
const direct = iconMap[name];
if (direct) return direct;
// Convert kebab-case to PascalCase (e.g., "database" or "database-icon" -> "DatabaseIcon")
const pascal = name
.split('-')
.map(part => part.charAt(0).toUpperCase() + part.slice(1))
.join('');
const pascalMatch = iconMap[pascal];
if (pascalMatch) return pascalMatch;

Copilot uses AI. Check for mistakes.
Comment on lines +507 to +516
{/* Home Dashboard — unified workspace landing page */}
<Route path="/home" element={
<AuthGuard fallback={<Navigate to="/login" />} loadingFallback={<LoadingScreen />}>
<ConnectedShell>
<Suspense fallback={<LoadingScreen />}>
<HomePage />
</Suspense>
</ConnectedShell>
</AuthGuard>
} />
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

This introduces a new top-level /home route and changes / to redirect there, but there are no accompanying console tests covering the new routing/empty-state behavior. Given the existing Vitest coverage in apps/console, please add tests verifying root redirect -> /home and that HomePage renders expected sections based on metadata/favorites/recent items.

Copilot uses AI. Check for mistakes.
- [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`
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

This roadmap entry claims full i18n support for the Home dashboard, but the new HomePage still contains several hard-coded English strings (e.g., loading + empty state copy/buttons). Either update the implementation to fully use i18n keys or soften this checklist item so the roadmap stays accurate.

Suggested change
- [x] **i18n support**All labels support internationalization via `useObjectTranslation`
- [x] **i18n wiring**Home dashboard uses `useObjectTranslation` for labels; remaining hard-coded loading/empty-state/button text is tracked as follow-up work

Copilot uses AI. Check for mistakes.
Comment on lines +35 to +38
const { apps, loading } = useMetadata();
const { recentItems } = useRecentItems();
const { favorites } = useFavorites();

Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

useMetadata() also exposes an error, but this page ignores it and will show the "no apps configured" empty state even if metadata loading failed. That can be misleading (and is a regression from the previous RootRedirect error state). Consider rendering an explicit error empty-state when error is set.

Copilot uses AI. Check for mistakes.
Comment on lines +63 to +86
<div className="min-h-screen flex items-center justify-center p-6">
<Empty>
<EmptyTitle>Welcome to ObjectUI</EmptyTitle>
<EmptyDescription>
Get started by creating your first application or configure your system settings.
</EmptyDescription>
<div className="mt-6 flex flex-col sm:flex-row items-center gap-3">
<Button
onClick={() => navigate('/create-app')}
data-testid="create-first-app-btn"
>
<Plus className="mr-2 h-4 w-4" />
Create Your First App
</Button>
<Button
variant="outline"
onClick={() => navigate('/system')}
data-testid="go-to-settings-btn"
>
<Settings className="mr-2 h-4 w-4" />
System Settings
</Button>
</div>
</Empty>
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

The empty state strings/buttons are hard-coded (e.g., "Welcome to ObjectUI", "Create Your First App", "System Settings") instead of using t(...). This conflicts with the claimed i18n support for the Home dashboard and will leave these strings untranslated.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

实现统一首页 Dashboard,实现多应用工作台和快速入口(对标 Airtable/Notion 模式)

3 participants