diff --git a/packages/app-shell/src/chrome/CommandPalette.tsx b/packages/app-shell/src/chrome/CommandPalette.tsx index 5756dd724..153d92b81 100644 --- a/packages/app-shell/src/chrome/CommandPalette.tsx +++ b/packages/app-shell/src/chrome/CommandPalette.tsx @@ -32,7 +32,7 @@ import { useRecordSearch } from '@object-ui/react'; import { useTheme } from './ThemeProvider'; import { useExpressionContext, evaluateVisibility } from '../providers/ExpressionProvider'; import { useObjectTranslation } from '@object-ui/i18n'; -import { resolveI18nLabel, getRecordDisplayName } from '../utils'; +import { resolveI18nLabel, getRecordDisplayName, appRouteSegment } from '../utils'; import { getIcon } from '../utils/getIcon'; import { useRecentItems } from '../context/RecentItemsProvider'; import { resolveHref } from '@object-ui/layout'; @@ -76,7 +76,7 @@ export function CommandPalette({ apps, activeApp, objects, onAppChange, dataSour if (!open) setInputValue(''); }, [open]); - const baseUrl = `/apps/${appName || activeApp?.name}`; + const baseUrl = `/apps/${appName || appRouteSegment(activeApp)}`; const { user } = useAuth(); const templateContext = useMemo(() => ({ currentUserId: user?.id ?? null }), [user?.id]); @@ -284,7 +284,7 @@ export function CommandPalette({ apps, activeApp, objects, onAppChange, dataSour runCommand(() => onAppChange(app.name))} + onSelect={() => runCommand(() => onAppChange(appRouteSegment(app) ?? app.name))} > {resolveI18nLabel(app.label, t)} diff --git a/packages/app-shell/src/layout/AppHeader.tsx b/packages/app-shell/src/layout/AppHeader.tsx index 59c483b6b..16427b0ad 100644 --- a/packages/app-shell/src/layout/AppHeader.tsx +++ b/packages/app-shell/src/layout/AppHeader.tsx @@ -61,7 +61,7 @@ import { useObjectTranslation, useObjectLabel } from '@object-ui/i18n'; import type { BreadcrumbItem as BreadcrumbItemType } from '@object-ui/types'; import { useAuth, getUserInitials } from '@object-ui/auth'; import { useMetadata } from '../providers/MetadataProvider'; -import { resolveI18nLabel, preferLocal, matchAppBySegment } from '../utils'; +import { resolveI18nLabel, preferLocal, matchAppBySegment, appRouteSegment } from '../utils'; import { getIcon } from '../utils/getIcon'; import { useMobileViewSwitcher } from './MobileViewSwitcherContext'; import { useNavigationContext } from '../context/NavigationContext'; @@ -856,7 +856,7 @@ export function AppHeader({ return ( navigate(`/apps/${app.name}`)} + onClick={() => navigate(`/apps/${appRouteSegment(app) ?? app.name}`)} className="cursor-pointer" > diff --git a/packages/app-shell/src/layout/AppSidebar.tsx b/packages/app-shell/src/layout/AppSidebar.tsx index 4ab7071cc..918ed1b96 100644 --- a/packages/app-shell/src/layout/AppSidebar.tsx +++ b/packages/app-shell/src/layout/AppSidebar.tsx @@ -61,7 +61,7 @@ import { usePermissions } from '@object-ui/permissions'; import { useRecentItems } from '../hooks/useRecentItems'; import { useFavorites } from '../hooks/useFavorites'; import { useNavPins } from '../hooks/useNavPins'; -import { resolveI18nLabel, matchAppBySegment } from '../utils'; +import { resolveI18nLabel, matchAppBySegment, appRouteSegment } from '../utils'; import { useObjectTranslation, useObjectLabel } from '@object-ui/i18n'; import { useAppContextSelectors } from './ContextSelectors'; @@ -271,7 +271,7 @@ export function AppSidebar({ activeAppName, onAppChange }: { activeAppName: stri [registeredObjectNames], ); - const basePath = activeApp ? `/apps/${activeAppName}` : ''; + const basePath = activeApp ? `/apps/${appRouteSegment(activeApp) ?? activeAppName}` : ''; // Fallback system navigation when no active app exists — routes into the Setup app. // The marketplace entry is hidden from non-admin members (install is gated to @@ -341,7 +341,7 @@ export function AppSidebar({ activeAppName, onAppChange }: { activeAppName: stri {activeApps.map((app: any) => ( onAppChange(app.name)} + onClick={() => onAppChange(appRouteSegment(app) ?? app.name)} className="gap-2 p-2" >
@@ -359,13 +359,13 @@ export function AppSidebar({ activeAppName, onAppChange }: { activeAppName: stri
{t('layout.appSwitcher.home')}
- navigate(`/apps/${activeAppName}/create-app`)} data-testid="add-app-btn"> + navigate(`/apps/${appRouteSegment(activeApp) ?? activeAppName}/create-app`)} data-testid="add-app-btn">
{t('layout.appSwitcher.addApp')}
- navigate(`/apps/${activeAppName}/edit-app/${activeAppName}`)} data-testid="edit-app-btn"> + navigate(`/apps/${appRouteSegment(activeApp) ?? activeAppName}/edit-app/${activeAppName}`)} data-testid="edit-app-btn">
@@ -655,7 +655,7 @@ export function AppSidebar({ activeAppName, onAppChange }: { activeAppName: stri } return leaves.slice(0, 5).map((item: any) => { const NavIcon = getIcon(item.icon); - const baseUrl = activeApp ? `/apps/${activeAppName}` : ''; + const baseUrl = activeApp ? `/apps/${appRouteSegment(activeApp) ?? activeAppName}` : ''; const { href } = resolveHref(item, baseUrl, { currentUserId: user?.id ?? null }); return ( diff --git a/packages/app-shell/src/layout/AppSwitcher.tsx b/packages/app-shell/src/layout/AppSwitcher.tsx index a501b4043..9d548af3f 100644 --- a/packages/app-shell/src/layout/AppSwitcher.tsx +++ b/packages/app-shell/src/layout/AppSwitcher.tsx @@ -17,7 +17,7 @@ import { } from '@object-ui/components'; import { ChevronDown, Check } from 'lucide-react'; import { useMetadata } from '../providers/MetadataProvider'; -import { resolveI18nLabel, matchAppBySegment } from '../utils'; +import { resolveI18nLabel, matchAppBySegment, appRouteSegment } from '../utils'; import { useObjectTranslation, useObjectLabel } from '@object-ui/i18n'; import { getIcon } from '../utils/getIcon'; @@ -63,7 +63,7 @@ export function AppSwitcher({ activeAppName, onAppChange }: AppSwitcherProps) { return ( onAppChange(app.name)} + onClick={() => onAppChange(appRouteSegment(app) ?? app.name)} className="flex items-center gap-2.5 py-2" >
diff --git a/packages/app-shell/src/layout/UnifiedSidebar.tsx b/packages/app-shell/src/layout/UnifiedSidebar.tsx index 582cfee42..1a146138e 100644 --- a/packages/app-shell/src/layout/UnifiedSidebar.tsx +++ b/packages/app-shell/src/layout/UnifiedSidebar.tsx @@ -47,7 +47,7 @@ import { useAuth, useIsWorkspaceAdmin } from '@object-ui/auth'; import { useRecentItems } from '../hooks/useRecentItems'; import { useFavorites } from '../hooks/useFavorites'; import { useNavPins } from '../hooks/useNavPins'; -import { resolveI18nLabel, matchAppBySegment } from '../utils'; +import { resolveI18nLabel, matchAppBySegment, appRouteSegment } from '../utils'; import { useObjectTranslation, useObjectLabel } from '@object-ui/i18n'; // useObjectLabel provides appLabel/appDescription for convention-based // i18n lookup — `{ns}.apps.{name}.label` resolves to the translated label @@ -255,7 +255,7 @@ export function UnifiedSidebar({ activeAppName }: UnifiedSidebarProps) { // Determine which navigation to show based on context const navigationItems = context === 'home' ? homeNavigation : appNavigation; - const basePath = context === 'app' && activeApp ? `/apps/${activeApp.name}` : ''; + const basePath = context === 'app' && activeApp ? `/apps/${appRouteSegment(activeApp)}` : ''; const isStudioApp = context === 'app' && activeApp?.name === 'studio'; const studioHomeSearch = React.useMemo(() => { if (!isStudioApp) return '';