Skip to content

Commit 3ee96aa

Browse files
committed
feat: update development plan status to complete
feat: enhance App component with CommandPalette and ErrorBoundary, and apply app branding feat: modify AppHeader for command palette integration and adjust mobile search button feat: update DashboardView to accept dataSource prop for improved data handling feat: enhance LoadingScreen with gradient background feat: update ObjectView to include filtering and sorting capabilities feat: modify PageView and ReportView to accept dataSource prop for better data management refactor: remove obsolete config.ts file fix: improve mock server initialization in browser.ts feat: add CommandPalette component for quick navigation feat: implement ErrorBoundary component for better error handling feat: create useBranding hook for dynamic app theming feat: add useObjectActions hook for streamlined CRUD operations
1 parent b2820e5 commit 3ee96aa

File tree

14 files changed

+762
-105
lines changed

14 files changed

+762
-105
lines changed

apps/console/DEVELOPMENT_PLAN.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# ObjectStack Console — Development Plan
22

33
> Generated: 2026-02-07
4-
> Status: **Active**
4+
> Status: **Complete** ✅ (All 10 phases executed)
55
66
## Current State
77

apps/console/src/App.tsx

Lines changed: 36 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,35 @@ import appConfig from '../objectstack.shared';
99

1010
// Components
1111
import { ConsoleLayout } from './components/ConsoleLayout';
12+
import { CommandPalette } from './components/CommandPalette';
13+
import { ErrorBoundary } from './components/ErrorBoundary';
1214
import { LoadingScreen } from './components/LoadingScreen';
1315
import { ObjectView } from './components/ObjectView';
1416
import { DashboardView } from './components/DashboardView';
1517
import { PageView } from './components/PageView';
1618
import { ReportView } from './components/ReportView';
1719
import { MetadataToggle, MetadataPanel, useMetadataInspector } from './components/MetadataInspector';
20+
import { useBranding } from './hooks/useBranding';
1821

1922
import { DetailView } from '@object-ui/plugin-detail';
2023
import { useParams } from 'react-router-dom';
2124

25+
/**
26+
* Patch: MSW discovery response uses 'routes' instead of 'endpoints'.
27+
* This normalizes the shape until @objectstack/plugin-msw is fixed upstream.
28+
*/
29+
function patchDiscoveryEndpoints(adapter: ObjectStackAdapter) {
30+
try {
31+
const client = adapter.getClient();
32+
const info = (client as any).discoveryInfo;
33+
if (info?.routes && !info.endpoints) {
34+
info.endpoints = info.routes;
35+
}
36+
} catch {
37+
// Silently ignore — adapter may not expose getClient()
38+
}
39+
}
40+
2241
// Detail View Component
2342
function RecordDetailView({ dataSource, objects, onEdit }: any) {
2443
const { objectName, recordId } = useParams();
@@ -99,24 +118,13 @@ export function AppContent() {
99118
const [editingRecord, setEditingRecord] = useState<any>(null);
100119
const [refreshKey, setRefreshKey] = useState(0);
101120

121+
// Apply app branding (primaryColor, favicon, title)
122+
useBranding(activeApp);
123+
102124
useEffect(() => {
103125
initializeDataSource();
104126
}, []);
105127

106-
// Sync title
107-
useEffect(() => {
108-
const favicon = activeApp?.branding?.favicon;
109-
if (favicon) {
110-
const link = document.querySelector<HTMLLinkElement>('#favicon');
111-
if (link) {
112-
link.href = favicon;
113-
}
114-
}
115-
if (activeApp?.label) {
116-
document.title = `${activeApp.label} - ObjectStack Console`;
117-
}
118-
}, [activeApp]);
119-
120128
async function initializeDataSource() {
121129
try {
122130
const adapter = new ObjectStackAdapter({
@@ -135,17 +143,11 @@ export function AppContent() {
135143
}
136144
});
137145

138-
await new Promise(resolve => setTimeout(resolve, 500));
139146
await adapter.connect();
140147

141-
// FIX: MSW Mock Server returns incorrect 'endpoints' in discovery.
142-
// The 'routes' property has the correct paths with /v1 prefix.
143-
const client = adapter.getClient();
144-
// @ts-ignore - accessing private property for fix
145-
if (client.discoveryInfo && client.discoveryInfo.routes) {
146-
// @ts-ignore - accessing private property for fix
147-
client.discoveryInfo.endpoints = client.discoveryInfo.routes;
148-
}
148+
// Patch: MSW discovery returns 'routes' instead of 'endpoints'.
149+
// TODO: Fix in @objectstack/plugin-msw to emit correct discovery shape.
150+
patchDiscoveryEndpoints(adapter);
149151

150152
setDataSource(adapter);
151153
} catch (err) {
@@ -208,7 +210,14 @@ export function AppContent() {
208210
objects={allObjects}
209211
connectionState={connectionState}
210212
>
213+
<CommandPalette
214+
apps={apps}
215+
activeApp={activeApp}
216+
objects={allObjects}
217+
onAppChange={handleAppChange}
218+
/>
211219
<SchemaRendererProvider dataSource={dataSource || {}}>
220+
<ErrorBoundary>
212221
<Routes>
213222
<Route path="/" element={
214223
// Redirect to first route within the app
@@ -241,15 +250,16 @@ export function AppContent() {
241250
} />
242251

243252
<Route path="dashboard/:dashboardName" element={
244-
<DashboardView />
253+
<DashboardView dataSource={dataSource} />
245254
} />
246255
<Route path="report/:reportName" element={
247-
<ReportView />
256+
<ReportView dataSource={dataSource} />
248257
} />
249258
<Route path="page/:pageName" element={
250-
<PageView />
259+
<PageView dataSource={dataSource} />
251260
} />
252261
</Routes>
262+
</ErrorBoundary>
253263

254264
<Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
255265
<DialogContent className="sm:max-w-xl max-h-[90vh] flex flex-col gap-0 p-0 overflow-hidden">

apps/console/src/components/AppHeader.tsx

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import {
77
BreadcrumbPage,
88
BreadcrumbSeparator,
99
SidebarTrigger,
10-
Input,
1110
Button,
1211
Separator,
1312
} from '@object-ui/components';
@@ -91,7 +90,7 @@ export function AppHeader({ appName, objects, connectionState }: { appName: stri
9190
</Breadcrumb>
9291

9392
{/* Mobile: Just show current page */}
94-
<span className="text-sm font-medium sm:hidden truncate max-w-[150px]">
93+
<span className="text-sm font-medium sm:hidden truncate max-w-37.5">
9594
{breadcrumbItems[breadcrumbItems.length - 1]?.label || appName}
9695
</span>
9796
</div>
@@ -100,17 +99,25 @@ export function AppHeader({ appName, objects, connectionState }: { appName: stri
10099
{/* Connection Status */}
101100
{connectionState && <ConnectionStatus state={connectionState} />}
102101

103-
{/* Search - Desktop */}
104-
<div className="hidden lg:flex relative">
105-
<Search className="absolute left-2.5 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
106-
<Input
107-
placeholder="Search..."
108-
className="w-[200px] xl:w-[280px] pl-8 h-8 bg-muted/50"
109-
/>
110-
</div>
102+
{/* Search - Desktop — opens ⌘K command palette */}
103+
<button
104+
onClick={() => document.dispatchEvent(new KeyboardEvent('keydown', { key: 'k', metaKey: true }))}
105+
className="hidden lg:flex relative items-center gap-2 w-50 xl:w-70 h-8 px-3 text-sm rounded-md border bg-muted/50 text-muted-foreground hover:bg-muted transition-colors"
106+
>
107+
<Search className="h-4 w-4" />
108+
<span className="flex-1 text-left">Search...</span>
109+
<kbd className="pointer-events-none inline-flex h-5 items-center gap-0.5 rounded border bg-muted px-1.5 text-[10px] font-medium text-muted-foreground">
110+
<span className="text-xs"></span>K
111+
</kbd>
112+
</button>
111113

112114
{/* Search button - Mobile/Tablet */}
113-
<Button variant="ghost" size="icon" className="lg:hidden h-8 w-8">
115+
<Button
116+
variant="ghost"
117+
size="icon"
118+
className="lg:hidden h-8 w-8"
119+
onClick={() => document.dispatchEvent(new KeyboardEvent('keydown', { key: 'k', metaKey: true }))}
120+
>
114121
<Search className="h-4 w-4" />
115122
</Button>
116123

0 commit comments

Comments
 (0)