diff --git a/.changeset/adr-0048-app-entry-links-sweep.md b/.changeset/adr-0048-app-entry-links-sweep.md new file mode 100644 index 000000000..5376066e6 --- /dev/null +++ b/.changeset/adr-0048-app-entry-links-sweep.md @@ -0,0 +1,16 @@ +--- +"@object-ui/app-shell": patch +"@object-ui/console": patch +--- + +ADR-0048: finish sweeping app-entry links onto the canonical package-id route +segment (follow-up to the home-page fix). + +- `AppManagementPage` (System → Apps) "Open app" button now opens + `/apps/` (`app._packageId ?? app.name`) instead of `/apps/`. +- `AppContent` current-app sub-routes/redirects (the `metadata/package` → + `component/developer/packages` redirect, and the record-form `baseUrl`) now + build against the URL's own `appName` segment instead of `activeApp.name`, so a + `/apps//…` URL keeps its package-id segment instead of flipping to + the name form. `requestedAppMissing` (preview-drafts) now resolves the segment + via `matchAppBySegment` so a package-id URL isn't treated as a missing app. diff --git a/apps/console/src/pages/system/AppManagementPage.tsx b/apps/console/src/pages/system/AppManagementPage.tsx index 7f7dd1732..bef5868b3 100644 --- a/apps/console/src/pages/system/AppManagementPage.tsx +++ b/apps/console/src/pages/system/AppManagementPage.tsx @@ -226,7 +226,9 @@ export function AppManagementPage() { size="icon" title="Open app" aria-label={`Open ${app.label || app.name}`} - onClick={() => navigate(`/apps/${app.name}`)} + // ADR-0048 — open via the canonical package-id route segment + // (falls back to name for runtime apps without a package id). + onClick={() => navigate(`/apps/${app._packageId ?? app.name}`)} > diff --git a/packages/app-shell/src/console/AppContent.tsx b/packages/app-shell/src/console/AppContent.tsx index 1220d58ca..a88a893c2 100644 --- a/packages/app-shell/src/console/AppContent.tsx +++ b/packages/app-shell/src/console/AppContent.tsx @@ -184,18 +184,22 @@ export function AppContent({ extraRoutes, extraRoutesNoApp }: AppContentProps = // silently preview the WRONG app; treat the request as "not ready" instead // and let the preview-specific empty state below say so. const requestedAppMissing = - previewDrafts && !!appName && !apps.some((a: any) => a.name === appName); + previewDrafts && !!appName && !matchAppBySegment(apps, appName); useEffect(() => { if (!activeApp?.name) return; - const packageMetadataPath = `/apps/${activeApp.name}/metadata/package`; + // ADR-0048 — build against the URL's own segment (`appName`, which may be the + // package id) so the match works and the redirect keeps the same segment; + // `activeApp.name` would flip a `/apps//…` URL to the name form. + const seg = appName ?? activeApp.name; + const packageMetadataPath = `/apps/${seg}/metadata/package`; if ( location.pathname === packageMetadataPath || location.pathname.startsWith(`${packageMetadataPath}/`) ) { - navigate(`/apps/${activeApp.name}/component/developer/packages`, { replace: true }); + navigate(`/apps/${seg}/component/developer/packages`, { replace: true }); } - }, [activeApp?.name, location.pathname, navigate]); + }, [activeApp?.name, appName, location.pathname, navigate]); const [isDialogOpen, setIsDialogOpen] = useState(false); const [editingRecord, setEditingRecord] = useState(null); @@ -331,7 +335,7 @@ export function AppContent({ extraRoutes, extraRoutesNoApp }: AppContentProps = // preserved for any object without the flag. const target = resolveRecordFormTarget({ objectDef: currentObjectDef as any, - baseUrl: activeApp?.name ? `/apps/${activeApp.name}` : '', + baseUrl: appName ? `/apps/${appName}` : (activeApp?.name ? `/apps/${activeApp.name}` : ''), record, }); if (target.kind === 'page') { @@ -470,7 +474,7 @@ export function AppContent({ extraRoutes, extraRoutesNoApp }: AppContentProps = score 16; declaration order breaks the tie). */} } + element={} /> } />