fix(nexus): auto-recover from unknown company prefix
When the URL contains a :companyPrefix segment that doesn't match any fetched company, Layout previously rendered a dead-end NotFoundPage with no recovery link. The sidebar's company switcher depends on selectedCompany being non-null, so users landing on a bogus prefix had no way back without hand-typing a new URL. Replace the NotFoundPage branch with a redirect to the same path under the first available company (selectedCompany ?? companies[0]). If the rest of the path is empty, fall back to /dashboard so the target is guaranteed to exist. The hasUnknownCompanyPrefix condition is already gated on companies.length > 0, so the fallback is reachable; the old NotFoundPage remains as a theoretical safety net if somehow both selectedCompany and companies[0] are null. Triggered by a user session after the embedded-postgres wipe: the browser had a stale localStorage selectedCompanyId and the user hand-typed URLs with guessed prefixes like /ASSISTANT/company/settings. Hitting any invalid prefix stranded them on the 404 with no UI to pick a different company. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
3d2117ee9f
commit
ab45bc063d
1 changed files with 26 additions and 5 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { BookOpen, MessageSquare, Moon, Settings, Sun } from "lucide-react";
|
import { BookOpen, MessageSquare, Moon, Settings, Sun } from "lucide-react";
|
||||||
import { Link, Outlet, useLocation, useNavigate, useParams } from "@/lib/router";
|
import { Link, Navigate, Outlet, useLocation, useNavigate, useParams } from "@/lib/router";
|
||||||
import { CompanyRail } from "./CompanyRail";
|
import { CompanyRail } from "./CompanyRail";
|
||||||
import { Sidebar } from "./Sidebar";
|
import { Sidebar } from "./Sidebar";
|
||||||
import { InstanceSidebar } from "./InstanceSidebar";
|
import { InstanceSidebar } from "./InstanceSidebar";
|
||||||
|
|
@ -460,10 +460,31 @@ export function Layout() {
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{hasUnknownCompanyPrefix ? (
|
{hasUnknownCompanyPrefix ? (
|
||||||
<NotFoundPage
|
// [nexus] Auto-recover from bogus URL prefixes by redirecting
|
||||||
scope="invalid_company_prefix"
|
// to the same path under the first available company, instead
|
||||||
requestedPrefix={companyPrefix ?? selectedCompany?.issuePrefix}
|
// of leaving the user stranded on an "Invite not available"
|
||||||
/>
|
// style dead end with no way back. hasUnknownCompanyPrefix is
|
||||||
|
// already gated on companies.length > 0, so the fallback is
|
||||||
|
// guaranteed to resolve. If no fallback exists for some reason,
|
||||||
|
// fall through to the old NotFoundPage.
|
||||||
|
(() => {
|
||||||
|
const fallbackCompany = selectedCompany ?? companies[0] ?? null;
|
||||||
|
if (!fallbackCompany) {
|
||||||
|
return (
|
||||||
|
<NotFoundPage
|
||||||
|
scope="invalid_company_prefix"
|
||||||
|
requestedPrefix={companyPrefix ?? undefined}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const restOfPath = location.pathname.replace(/^\/[^/]+/, "") || "/dashboard";
|
||||||
|
return (
|
||||||
|
<Navigate
|
||||||
|
to={`/${fallbackCompany.issuePrefix}${restOfPath}${location.search}${location.hash}`}
|
||||||
|
replace
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})()
|
||||||
) : (
|
) : (
|
||||||
<Outlet />
|
<Outlet />
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue