feat: scope workspace routes by organization slug

This commit is contained in:
2026-07-02 20:11:57 -07:00
parent fae97a7046
commit 3ab0e3e034
48 changed files with 565 additions and 96 deletions

View File

@@ -11,7 +11,8 @@ import { Card } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Select } from "@/components/ui/select";
import type { ApiResult } from "@/modules/core/server/api";
import type { Organization } from "@/modules/core/types";
import type { AccountOrganizationOption, Organization } from "@/modules/core/types";
import { getWorkspaceHref, getWorkspacePathFromPathname } from "@/modules/shared/lib/workspace-routing";
type AuthMode = "login" | "register" | "setup";
@@ -45,6 +46,27 @@ async function readApiResult<T extends Record<string, unknown>>(response: Respon
}
}
type SessionPayload = {
account: unknown | null;
organization: Organization | null;
organizations: AccountOrganizationOption[];
};
function getSessionWorkspaceHref(session: SessionPayload, preferredHref: string): string {
const activeOrganization = session.organization ?? session.organizations.find((organization) => organization.isActive);
return getWorkspaceHref(activeOrganization?.slug, getWorkspacePathFromPathname(preferredHref));
}
async function resolveWorkspaceRedirect(preferredHref: string): Promise<string> {
const sessionResponse = await fetch("/api/auth/session");
const session = await readApiResult<SessionPayload>(sessionResponse);
if (!session.success) {
return preferredHref;
}
return getSessionWorkspaceHref(session, preferredHref);
}
function AuthPanelContent({ mode }: AuthPanelProps): React.ReactElement {
const router = useRouter();
const searchParams = useSearchParams();
@@ -69,7 +91,7 @@ function AuthPanelContent({ mode }: AuthPanelProps): React.ReactElement {
fetch("/api/organizations"),
]);
const bootstrap = await readApiResult<{ setupRequired: boolean }>(bootstrapResponse);
const session = await readApiResult<{ account: unknown | null }>(sessionResponse);
const session = await readApiResult<SessionPayload>(sessionResponse);
const organizationsResult = await readApiResult<{ organizations: Organization[] }>(organizationsResponse);
if (!isMounted || !bootstrap.success || !session.success) {
@@ -97,7 +119,7 @@ function AuthPanelContent({ mode }: AuthPanelProps): React.ReactElement {
}
if (session.account) {
router.replace("/app/dashboard");
router.replace(getSessionWorkspaceHref(session, "/app/dashboard"));
}
}
@@ -159,7 +181,7 @@ function AuthPanelContent({ mode }: AuthPanelProps): React.ReactElement {
}
router.refresh();
router.replace(isLogin ? redirectTo : "/app/dashboard");
router.replace(await resolveWorkspaceRedirect(isLogin ? redirectTo : "/app/dashboard"));
}
if (hasAccounts === null) {