Add risk assessments tab to risk list page
Surface the organization risk assessments list alongside the risk register by adding a shared Risks/Risk assessments tab bar to both list pages. Signed-off-by: Sacha Al Himdani <sacha@probo.com>
This commit is contained in:
@@ -1,37 +0,0 @@
|
||||
// Copyright (c) 2026 Probo Inc <hello@probo.com>.
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
import { lazy } from "@probo/react-lazy";
|
||||
import type { AppRoute } from "@probo/routes";
|
||||
|
||||
import { PageSkeleton } from "#/components/skeletons/PageSkeleton";
|
||||
|
||||
export const riskAssessmentRoutes = [
|
||||
{
|
||||
path: "risk-assessments",
|
||||
Fallback: PageSkeleton,
|
||||
Component: lazy(
|
||||
() =>
|
||||
import("./RiskAssessmentsPageLoader"),
|
||||
),
|
||||
},
|
||||
{
|
||||
path: "risk-assessments/:riskAssessmentId",
|
||||
Fallback: PageSkeleton,
|
||||
Component: lazy(
|
||||
() =>
|
||||
import("./RiskAssessmentDetailPageLoader"),
|
||||
),
|
||||
},
|
||||
] satisfies AppRoute[];
|
||||
@@ -0,0 +1,43 @@
|
||||
// Copyright (c) 2026 Probo Inc <hello@probo.com>.
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
import { Tabs } from "@probo/ui";
|
||||
import { Suspense, useEffect } from "react";
|
||||
import { useQueryLoader } from "react-relay";
|
||||
import { Outlet } from "react-router";
|
||||
|
||||
import type { RisksTabsQuery } from "#/__generated__/core/RisksTabsQuery.graphql";
|
||||
import { useOrganizationId } from "#/hooks/useOrganizationId";
|
||||
|
||||
import { RisksTabs, risksTabsQuery } from "./RisksTabs";
|
||||
|
||||
export default function RisksLayoutLoader() {
|
||||
const organizationId = useOrganizationId();
|
||||
const [queryRef, loadQuery]
|
||||
= useQueryLoader<RisksTabsQuery>(risksTabsQuery);
|
||||
|
||||
useEffect(() => {
|
||||
loadQuery({ organizationId });
|
||||
}, [loadQuery, organizationId]);
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<Suspense fallback={<Tabs />}>
|
||||
{queryRef && <RisksTabs queryRef={queryRef} />}
|
||||
</Suspense>
|
||||
|
||||
<Outlet />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
61
apps/console/src/pages/organizations/risks/RisksTabs.tsx
Normal file
61
apps/console/src/pages/organizations/risks/RisksTabs.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
// Copyright (c) 2026 Probo Inc <hello@probo.com>.
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import { TabLink, Tabs } from "@probo/ui";
|
||||
import { graphql, type PreloadedQuery, usePreloadedQuery } from "react-relay";
|
||||
|
||||
import type { RisksTabsQuery } from "#/__generated__/core/RisksTabsQuery.graphql";
|
||||
import { useOrganizationId } from "#/hooks/useOrganizationId";
|
||||
|
||||
export const risksTabsQuery = graphql`
|
||||
query RisksTabsQuery($organizationId: ID!) {
|
||||
organization: node(id: $organizationId) {
|
||||
... on Organization {
|
||||
canListRisks: permission(action: "core:risk:list")
|
||||
canListRiskAssessments: permission(action: "core:risk-assessment:list")
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
interface RisksTabsProps {
|
||||
queryRef: PreloadedQuery<RisksTabsQuery>;
|
||||
}
|
||||
|
||||
export function RisksTabs({ queryRef }: RisksTabsProps) {
|
||||
const { __ } = useTranslate();
|
||||
const organizationId = useOrganizationId();
|
||||
const data = usePreloadedQuery<RisksTabsQuery>(risksTabsQuery, queryRef);
|
||||
|
||||
const canListRisks = data.organization?.canListRisks ?? false;
|
||||
const canListRiskAssessments
|
||||
= data.organization?.canListRiskAssessments ?? false;
|
||||
const baseUrl = `/organizations/${organizationId}`;
|
||||
|
||||
return (
|
||||
<Tabs>
|
||||
{canListRisks && (
|
||||
<TabLink to={`${baseUrl}/risks`} end>
|
||||
{__("Risks")}
|
||||
</TabLink>
|
||||
)}
|
||||
{canListRiskAssessments && (
|
||||
<TabLink to={`${baseUrl}/risk-assessments`} end>
|
||||
{__("Risk assessments")}
|
||||
</TabLink>
|
||||
)}
|
||||
</Tabs>
|
||||
);
|
||||
}
|
||||
@@ -23,9 +23,23 @@ import { RisksPageSkeleton } from "#/components/skeletons/RisksPageSkeleton";
|
||||
|
||||
export const riskRoutes = [
|
||||
{
|
||||
path: "risks",
|
||||
Fallback: RisksPageSkeleton,
|
||||
Component: lazy(() => import("./RisksPageLoader")),
|
||||
Fallback: PageSkeleton,
|
||||
Component: lazy(() => import("./RisksLayoutLoader")),
|
||||
children: [
|
||||
{
|
||||
path: "risks",
|
||||
Fallback: RisksPageSkeleton,
|
||||
Component: lazy(() => import("./RisksPageLoader")),
|
||||
},
|
||||
{
|
||||
path: "risk-assessments",
|
||||
Fallback: PageSkeleton,
|
||||
Component: lazy(
|
||||
() =>
|
||||
import("./risk-assessments/RiskAssessmentsPageLoader"),
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "risks/:riskId",
|
||||
@@ -72,4 +86,12 @@ export const riskRoutes = [
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "risk-assessments/:riskAssessmentId",
|
||||
Fallback: PageSkeleton,
|
||||
Component: lazy(
|
||||
() =>
|
||||
import("./risk-assessments/RiskAssessmentDetailPageLoader"),
|
||||
),
|
||||
},
|
||||
] satisfies AppRoute[];
|
||||
|
||||
@@ -31,7 +31,6 @@ import { ViewerLayoutLoading } from "./pages/iam/memberships/ViewerLayoutLoading
|
||||
import { peopleRoutes } from "./pages/iam/organizations/people/routes";
|
||||
import { compliancePageRoutes } from "./pages/organizations/compliance-page/routes";
|
||||
import { cookieBannerRoutes } from "./pages/organizations/cookie-banners/routes";
|
||||
import { riskAssessmentRoutes } from "./pages/organizations/risk-assessments/routes";
|
||||
import { riskRoutes } from "./pages/organizations/risks/routes";
|
||||
import { thirdPartyRoutes } from "./pages/organizations/third-parties/routes";
|
||||
import { CurrentUser } from "./providers/CurrentUser";
|
||||
@@ -302,7 +301,6 @@ const routes = [
|
||||
},
|
||||
...peopleRoutes,
|
||||
...riskRoutes,
|
||||
...riskAssessmentRoutes,
|
||||
...measureRoutes,
|
||||
...documentsRoutes,
|
||||
...thirdPartyRoutes,
|
||||
|
||||
Reference in New Issue
Block a user