Create assume page to redirect to when AssumptionRequired catched in boundary

Signed-off-by: Émile Ré <emile@getprobo.com>
This commit is contained in:
Émile Ré
2026-02-10 15:01:52 +04:00
parent ab497ad6a7
commit 5fa4477d5d
5 changed files with 136 additions and 94 deletions

View File

@@ -15,7 +15,7 @@ export function OrganizationErrorBoundary() {
}
if (error instanceof AssumptionRequiredError) {
return <Navigate to="/auth/login" state={{ from: location.pathname, organizationId }} />;
return <Navigate to={`/organizations/${organizationId}/assume`} state={{ from: location.pathname }} />;
}
return <PageError error={error instanceof Error ? error : new Error("unknown error")} />;

View File

@@ -1,9 +1,12 @@
import { Card, Logo } from "@probo/ui";
import type { PropsWithChildren } from "react";
import { Outlet } from "react-router";
import { IAMRelayProvider } from "#/providers/IAMRelayProvider";
export default function AuthLayout() {
export default function AuthLayout(props: PropsWithChildren) {
const { children } = props;
return (
<div className="min-h-screen text-txt-primary bg-level-0 flex flex-col items-center justify-center">
<Card className="w-full max-w-lg px-12 py-8 flex flex-col items-center justify-center">
@@ -12,7 +15,7 @@ export default function AuthLayout() {
<div className="w-full border-t border-t-border-mid" />
</div>
<IAMRelayProvider>
<Outlet />
{children ?? <Outlet />}
</IAMRelayProvider>
</Card>
</div>

View File

@@ -0,0 +1,35 @@
import { useTranslate } from "@probo/i18n";
import { useLocation, useNavigate } from "react-router";
import { useAssume } from "#/hooks/iam/useAssume";
import AuthLayout from "../auth/AuthLayout";
interface State {
from: string;
}
export default function AssumePage() {
const navigate = useNavigate();
const location = useLocation();
const state = location.state as State;
const { __ } = useTranslate();
useAssume({
onSuccess: () => void navigate(state.from),
});
return (
<AuthLayout>
<div className="space-y-6 w-full max-w-md mx-auto pt-8">
<div className="space-y-2 text-center">
<h1 className="text-3xl font-bold">{__("Sign in Redirection")}</h1>
<p className="text-txt-tertiary">
{__("Redirecting you to your authentication URL…")}
</p>
</div>
</div>
</AuthLayout>
);
}

View File

@@ -120,119 +120,127 @@ const routes = [
() => import("./pages/DocumentSigningRequestsPage"),
),
},
{
path: "/organizations/:organizationId/employee",
Component: lazy(
() => import("./pages/organizations/employee/EmployeeLayoutLoader"),
),
ErrorBoundary: OrganizationErrorBoundary,
children: [
{
index: true,
Component: lazy(
() =>
import("./pages/organizations/employee/EmployeeDocumentsPageLoader"),
),
},
{
path: ":documentId",
Component: lazy(
() =>
import("./pages/organizations/employee/EmployeeDocumentSignaturePageLoader"),
),
},
],
},
{
path: "/organizations/:organizationId",
Component: lazy(
() => import("./pages/iam/organizations/ViewerMembershipLayoutLoader"),
),
ErrorBoundary: OrganizationErrorBoundary,
children: [
{
path: "",
Component: () => {
const { role } = use(CurrentUser);
switch (role) {
case Role.EMPLOYEE:
return <Navigate to="employee" />;
case Role.AUDITOR:
return <Navigate to="measures" />;
default:
return <Navigate to="tasks" />;
}
},
path: "assume",
Component: lazy(() => import("./pages/iam/organizations/AssumePage")),
},
{
path: "settings",
Fallback: PageSkeleton,
path: "employee",
Component: lazy(
() => import("./pages/iam/organizations/settings/SettingsLayout"),
() => import("./pages/organizations/employee/EmployeeLayoutLoader"),
),
ErrorBoundary: OrganizationErrorBoundary,
children: [
{
index: true,
loader: () => {
// eslint-disable-next-line
throw redirect("general");
},
},
{
path: "general",
Component: lazy(
() =>
import("./pages/iam/organizations/settings/GeneralSettingsPageLoader"),
import("./pages/organizations/employee/EmployeeDocumentsPageLoader"),
),
},
{
path: "members",
path: ":documentId",
Component: lazy(
() =>
import("./pages/iam/organizations/settings/MembersPageLoader"),
),
},
{
path: "saml-sso",
Component: lazy(
() =>
import("./pages/iam/organizations/settings/SAMLSettingsPageLoader"),
),
},
{
path: "scim",
Component: lazy(
() =>
import("./pages/iam/organizations/settings/SCIMSettingsPageLoader"),
import("./pages/organizations/employee/EmployeeDocumentSignaturePageLoader"),
),
},
],
},
...riskRoutes,
...measureRoutes,
...documentsRoutes,
...peopleRoutes,
...vendorRoutes,
...frameworkRoutes,
...taskRoutes,
...assetRoutes,
...dataRoutes,
...auditRoutes,
...meetingsRoutes,
...nonconformityRoutes,
...obligationRoutes,
...continualImprovementRoutes,
...rightsRequestRoutes,
...processingActivityRoutes,
...statesOfApplicabilityRoutes,
...compliancePageRoutes,
...snapshotsRoutes,
{
path: "*",
Component: PageError,
Component: lazy(
() => import("./pages/iam/organizations/ViewerMembershipLayoutLoader"),
),
ErrorBoundary: OrganizationErrorBoundary,
children: [
{
Component: () => {
const { role } = use(CurrentUser);
switch (role) {
case Role.EMPLOYEE:
return <Navigate to="employee" />;
case Role.AUDITOR:
return <Navigate to="measures" />;
default:
return <Navigate to="tasks" />;
}
},
},
{
path: "settings",
Fallback: PageSkeleton,
Component: lazy(
() => import("./pages/iam/organizations/settings/SettingsLayout"),
),
children: [
{
index: true,
loader: () => {
// eslint-disable-next-line
throw redirect("general");
},
},
{
path: "general",
Component: lazy(
() =>
import("./pages/iam/organizations/settings/GeneralSettingsPageLoader"),
),
},
{
path: "members",
Component: lazy(
() =>
import("./pages/iam/organizations/settings/MembersPageLoader"),
),
},
{
path: "saml-sso",
Component: lazy(
() =>
import("./pages/iam/organizations/settings/SAMLSettingsPageLoader"),
),
},
{
path: "scim",
Component: lazy(
() =>
import("./pages/iam/organizations/settings/SCIMSettingsPageLoader"),
),
},
],
},
...riskRoutes,
...measureRoutes,
...documentsRoutes,
...peopleRoutes,
...vendorRoutes,
...frameworkRoutes,
...taskRoutes,
...assetRoutes,
...dataRoutes,
...auditRoutes,
...meetingsRoutes,
...nonconformityRoutes,
...obligationRoutes,
...continualImprovementRoutes,
...rightsRequestRoutes,
...processingActivityRoutes,
...statesOfApplicabilityRoutes,
...compliancePageRoutes,
...snapshotsRoutes,
{
path: "*",
Component: PageError,
},
],
},
],
},
// Fallback URL to the NotFound Page
{
path: "*",