Skip to content

Commit 3733ef5

Browse files
committed
feat: Enhance AppSwitcher to utilize remote app data and improve app registry handling
1 parent e8c5d25 commit 3733ef5

3 files changed

Lines changed: 83 additions & 29 deletions

File tree

apps/web/src/components/dashboard/AppSwitcher.tsx

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ import {
1818
SidebarMenuItem,
1919
useSidebar,
2020
} from '@/components/ui/sidebar';
21-
import { mockApps } from '@/lib/app-registry';
21+
import { useAppList } from '@/hooks/use-metadata';
22+
import { consoleApp, mockApps, toRegistryEntry } from '@/lib/app-registry';
23+
import type { AppRegistryEntry } from '@/lib/app-registry';
2224

2325
interface AppSwitcherProps {
2426
/** "sidebar" = full sidebar header style, "topbar" = compact button for top bar */
@@ -29,29 +31,37 @@ function useAppSwitcherState() {
2931
const navigate = useNavigate();
3032
const { pathname } = useLocation();
3133
const [query, setQuery] = useState('');
34+
const { data: remoteApps } = useAppList();
35+
36+
// Build the app list: Console (always) + remote apps converted to registry entries.
37+
// Falls back to mockApps when the server is unreachable (useAppList returns mockAppDefinitions).
38+
const apps: AppRegistryEntry[] = useMemo(() => {
39+
if (!remoteApps || remoteApps.length === 0) return mockApps;
40+
return [consoleApp, ...remoteApps.map(toRegistryEntry)];
41+
}, [remoteApps]);
3242

3343
const normalizedQuery = query.trim().toLowerCase();
3444

3545
const activeApp = useMemo(() => {
3646
if (pathname.startsWith('/apps/')) {
3747
const [, , appId] = pathname.split('/');
38-
return mockApps.find((app) => app.id === appId);
48+
return apps.find((app) => app.id === appId);
3949
}
4050
if (pathname.startsWith('/settings')) {
41-
return mockApps.find((app) => app.id === 'console');
51+
return apps.find((app) => app.id === 'console');
4252
}
43-
return mockApps.find((app) => app.id === 'console');
44-
}, [pathname]);
53+
return apps.find((app) => app.id === 'console');
54+
}, [pathname, apps]);
4555

4656
const displayName = activeApp?.name ?? 'Console';
4757

4858
const filteredApps = useMemo(() => {
49-
if (!normalizedQuery) return mockApps;
50-
return mockApps.filter((app) => {
59+
if (!normalizedQuery) return apps;
60+
return apps.filter((app) => {
5161
const haystack = `${app.name} ${app.description}`.toLowerCase();
5262
return haystack.includes(normalizedQuery);
5363
});
54-
}, [normalizedQuery]);
64+
}, [normalizedQuery, apps]);
5565

5666
const pinnedApps = filteredApps.filter((app) => app.pinned);
5767
const systemApps = filteredApps.filter(
@@ -87,7 +97,7 @@ function AppDropdownBody({
8797
customApps,
8898
filteredApps,
8999
}: ReturnType<typeof useAppSwitcherState>) {
90-
const renderAppItem = (app: (typeof mockApps)[number]) => (
100+
const renderAppItem = (app: AppRegistryEntry) => (
91101
<DropdownMenuItem
92102
key={app.id}
93103
className="flex items-start gap-2"

apps/web/src/lib/app-registry.ts

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import type { AppDefinition } from '@/types/metadata';
2+
13
export type AppRegistryEntry = {
24
id: string;
35
name: string;
@@ -8,16 +10,39 @@ export type AppRegistryEntry = {
810
pinned?: boolean;
911
};
1012

13+
/**
14+
* Convert an AppDefinition (from remote metadata API) to an AppRegistryEntry
15+
* used by the AppSwitcher UI.
16+
*/
17+
export function toRegistryEntry(app: AppDefinition): AppRegistryEntry {
18+
return {
19+
id: app.name,
20+
name: app.label || app.name,
21+
description: app.description || '',
22+
href: `/apps/${app.name}`,
23+
category: 'business',
24+
status: app.active !== false ? 'active' : 'paused',
25+
};
26+
}
27+
28+
/**
29+
* Static Console entry — always present regardless of remote data.
30+
*/
31+
export const consoleApp: AppRegistryEntry = {
32+
id: 'console',
33+
name: 'Console',
34+
description: 'System administration and settings.',
35+
href: '/settings',
36+
category: 'system',
37+
status: 'active',
38+
pinned: true,
39+
};
40+
41+
/**
42+
* Fallback mock apps used when the server is unreachable.
43+
*/
1144
export const mockApps: AppRegistryEntry[] = [
12-
{
13-
id: 'console',
14-
name: 'Console',
15-
description: 'System administration and settings.',
16-
href: '/settings',
17-
category: 'system',
18-
status: 'active',
19-
pinned: true,
20-
},
45+
consoleApp,
2146
{
2247
id: 'crm',
2348
name: 'CRM',

objectstack.config.ts

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
/**
22
* ObjectStack Configuration
3-
*
3+
*
44
* Configuration file for @objectstack/cli serve command.
55
* This defines the kernel configuration, plugins, and metadata sources.
66
*/
77

8+
import { defineStack } from '@objectstack/spec';
9+
import { AppPlugin, DriverPlugin } from '@objectstack/runtime';
10+
import { ObjectQLPlugin } from '@objectstack/objectql';
11+
import { InMemoryDriver } from '@objectstack/driver-memory';
812
import { AuditLogPlugin } from '@objectos/audit';
913
import { BetterAuthPlugin } from '@objectos/auth';
1014
import { AutomationPlugin } from '@objectos/automation';
@@ -19,7 +23,20 @@ import { StoragePlugin } from '@objectos/storage';
1923
import { WorkflowPlugin } from '@objectos/workflow';
2024
import { resolve } from 'path';
2125

22-
export default {
26+
// ─── Example App Bundles ─────────────────────────────────────────
27+
import CrmApp from './examples/crm/objectstack.config';
28+
import TodoApp from './examples/todo/objectstack.config';
29+
30+
export default defineStack({
31+
manifest: {
32+
id: 'com.objectos.platform',
33+
name: 'ObjectOS',
34+
namespace: 'objectos',
35+
version: '0.1.0',
36+
type: 'app',
37+
description: 'ObjectOS Business Operating System',
38+
},
39+
2340
/**
2441
* Metadata sources
2542
* Define where to load object schemas, permissions, workflows, etc.
@@ -33,20 +50,18 @@ export default {
3350
]
3451
},
3552

36-
/**
37-
* Objects configuration
38-
* Can define objects inline or reference external files
39-
*/
40-
objects: {},
41-
4253
/**
4354
* Plugins to check/load
4455
* These are initialized in order during kernel bootstrap
4556
*/
4657
plugins: [
58+
// ObjectQL Engine & Driver
59+
new ObjectQLPlugin(),
60+
new DriverPlugin(new InMemoryDriver()),
61+
4762
// Foundation
4863
new MetricsPlugin(),
49-
new CachePlugin(),
64+
new CachePlugin(),
5065
new StoragePlugin(),
5166

5267
// Core
@@ -58,11 +73,15 @@ export default {
5873
new WorkflowPlugin(),
5974
new AutomationPlugin(),
6075
new JobsPlugin(),
61-
76+
6277
// Services
6378
new NotificationPlugin(),
6479
new I18nPlugin(),
6580
// createRealtimePlugin(),
81+
82+
// Example Apps
83+
new AppPlugin(CrmApp),
84+
new AppPlugin(TodoApp),
6685
],
6786

6887
/**
@@ -93,4 +112,4 @@ export default {
93112
level: process.env.LOG_LEVEL || 'info',
94113
}
95114
}
96-
};
115+
} as any);

0 commit comments

Comments
 (0)