Skip to content

Commit 0a28ed4

Browse files
Copilothotlong
andcommitted
fix: support all navigation modes (page/new_window/split/popover) in ListView
- Fix page mode: Console onNavigate now uses React Router navigate() for record detail - Fix new_window: useNavigationOverlay delegates to onNavigate when available for correct URL - Fix split mode: Console passes mainContent to NavigationOverlay for split layout - Fix popover mode: NavigationOverlay falls back to Dialog when no popoverTrigger provided - Fix plugin-view: Add split/popover branches in handleRowClick Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
1 parent cce0d5d commit 0a28ed4

4 files changed

Lines changed: 96 additions & 6 deletions

File tree

apps/console/src/components/ObjectView.tsx

Lines changed: 65 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -425,10 +425,19 @@ export function ObjectView({ dataSource, objects, onEdit, onRowClick }: any) {
425425
const navOverlay = useNavigationOverlay({
426426
navigation: detailNavigation,
427427
objectName: objectDef.name,
428-
onNavigate: (recordId: string | number, _action?: string) => {
429-
const newParams = new URLSearchParams(searchParams);
430-
newParams.set('recordId', String(recordId));
431-
setSearchParams(newParams);
428+
onNavigate: (recordId: string | number, action?: string) => {
429+
if (action === 'new_window') {
430+
// Open record detail in a new browser tab with Console-correct URL
431+
const basePath = window.location.pathname.replace(/\/view\/.*$/, '');
432+
window.open(`${basePath}/record/${String(recordId)}`, '_blank');
433+
return;
434+
}
435+
// page / view mode — navigate to record detail page
436+
if (viewId) {
437+
navigate(`../../record/${String(recordId)}`, { relative: 'path' });
438+
} else {
439+
navigate(`record/${String(recordId)}`);
440+
}
432441
},
433442
});
434443
const handleDrawerClose = () => {
@@ -633,9 +642,15 @@ export function ObjectView({ dataSource, objects, onEdit, onRowClick }: any) {
633642
onNavigate: (recordId: string | number, mode: 'view' | 'edit') => {
634643
if (mode === 'edit') {
635644
onEdit?.({ _id: recordId, id: recordId });
645+
} else if (mode === 'view') {
646+
if (viewId) {
647+
navigate(`../../record/${String(recordId)}`, { relative: 'path' });
648+
} else {
649+
navigate(`record/${String(recordId)}`);
650+
}
636651
}
637652
},
638-
}), [objectDef.name, onEdit, activeView?.showSearch, activeView?.showFilters, activeView?.showSort]);
653+
}), [objectDef.name, onEdit, activeView?.showSearch, activeView?.showFilters, activeView?.showSort, navigate, viewId]);
639654

640655
return (
641656
<div className="h-full flex flex-col bg-background min-w-0 overflow-hidden">
@@ -767,6 +782,48 @@ export function ObjectView({ dataSource, objects, onEdit, onRowClick }: any) {
767782

768783
{/* 2. Content — Plugin ObjectView with ViewSwitcher + Filter + Sort */}
769784
<div className="flex-1 overflow-hidden relative flex flex-row">
785+
{navOverlay.mode === 'split' && navOverlay.isOpen ? (
786+
<NavigationOverlay
787+
{...navOverlay}
788+
setIsOpen={(open: boolean) => { if (!open) handleDrawerClose(); }}
789+
title={objectDef.label}
790+
mainContent={
791+
<div className="flex-1 min-w-0 relative h-full flex flex-col">
792+
<div className="flex-1 relative overflow-auto p-3 sm:p-4">
793+
<PluginObjectView
794+
schema={objectViewSchema}
795+
dataSource={dataSource}
796+
views={mergedViews}
797+
activeViewId={activeViewId}
798+
onViewChange={handleViewChange}
799+
onEdit={(record: any) => onEdit?.(record)}
800+
onRowClick={onRowClick || ((record: any) => {
801+
navOverlay.handleClick(record);
802+
})}
803+
renderListView={renderListView}
804+
/>
805+
</div>
806+
{typeof recordCount === 'number' && (
807+
<div data-testid="record-count-footer" className="border-t px-3 sm:px-4 py-1.5 text-xs text-muted-foreground bg-muted/5 shrink-0">
808+
{t('console.objectView.recordCount', { count: recordCount })}
809+
</div>
810+
)}
811+
</div>
812+
}
813+
>
814+
{(record: Record<string, unknown>) => {
815+
const recordId = (record._id || record.id) as string;
816+
return (
817+
<DrawerDetailContent
818+
objectDef={objectDef}
819+
recordId={recordId}
820+
dataSource={dataSource}
821+
onEdit={onEdit}
822+
/>
823+
);
824+
}}
825+
</NavigationOverlay>
826+
) : (
770827
<div className="flex-1 min-w-0 relative h-full flex flex-col">
771828
<div className="flex-1 relative overflow-auto p-3 sm:p-4">
772829
<PluginObjectView
@@ -789,6 +846,7 @@ export function ObjectView({ dataSource, objects, onEdit, onRowClick }: any) {
789846
</div>
790847
)}
791848
</div>
849+
)}
792850
{/* Metadata panel only shows for admin users */}
793851
<MetadataPanel
794852
open={showDebug && isAdmin}
@@ -819,6 +877,7 @@ export function ObjectView({ dataSource, objects, onEdit, onRowClick }: any) {
819877
</div>
820878

821879
{/* Record Detail Overlay — navigation mode driven by objectDef.navigation */}
880+
{navOverlay.mode !== 'split' && (
822881
<NavigationOverlay
823882
{...navOverlay}
824883
setIsOpen={(open: boolean) => { if (!open) handleDrawerClose(); }}
@@ -837,6 +896,7 @@ export function ObjectView({ dataSource, objects, onEdit, onRowClick }: any) {
837896
);
838897
}}
839898
</NavigationOverlay>
899+
)}
840900
</div>
841901
);
842902
}

packages/components/src/custom/navigation-overlay.tsx

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,26 @@ export const NavigationOverlay: React.FC<NavigationOverlayProps> = ({
285285

286286
// --- Popover Mode ---
287287
if (mode === 'popover') {
288+
if (!popoverTrigger) {
289+
// Fallback: render as a compact floating card when no trigger element is provided
290+
if (!isOpen) return null;
291+
return (
292+
<Dialog open={isOpen} onOpenChange={setIsOpen}>
293+
<DialogContent
294+
className={cn('w-96 max-h-[80vh] overflow-y-auto p-4', className)}
295+
style={widthStyle}
296+
>
297+
<DialogHeader>
298+
<DialogTitle className="text-sm">{resolvedTitle}</DialogTitle>
299+
{description && <DialogDescription className="text-xs">{description}</DialogDescription>}
300+
</DialogHeader>
301+
<div className="mt-2">
302+
{renderContent(selectedRecord)}
303+
</div>
304+
</DialogContent>
305+
</Dialog>
306+
);
307+
}
288308
return (
289309
<Popover open={isOpen} onOpenChange={setIsOpen}>
290310
{popoverTrigger && (

packages/plugin-view/src/ObjectView.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,12 @@ export const ObjectView: React.FC<ObjectViewProps> = ({
425425
}
426426
return;
427427
}
428+
if (navigationConfig.mode === 'split' || navigationConfig.mode === 'popover') {
429+
setFormMode('view');
430+
setSelectedRecord(record);
431+
setIsFormOpen(true);
432+
return;
433+
}
428434
}
429435

430436
// Default behavior

packages/react/src/hooks/useNavigationOverlay.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,9 +139,13 @@ export function useNavigationOverlay(
139139
return;
140140
}
141141

142-
// new_window / openNewTab — open in new browser tab
142+
// new_window / openNewTab — delegate to onNavigate when available, else open directly
143143
if (mode === 'new_window' || navigation.openNewTab) {
144144
const recordId = record._id || record.id;
145+
if (onNavigate && recordId != null) {
146+
onNavigate(recordId as string | number, 'new_window');
147+
return;
148+
}
145149
const viewPath = view ? `/${view}` : '';
146150
const url = objectName ? `/${objectName}/${recordId}${viewPath}` : `/${recordId}${viewPath}`;
147151
window.open(url, '_blank');

0 commit comments

Comments
 (0)