From 0f1b32f51f58d5cbc71b9a258aed297caf1b1f89 Mon Sep 17 00:00:00 2001 From: timbastin Date: Fri, 12 Jul 2024 17:14:35 +0200 Subject: [PATCH 01/17] improves risk handling, adds user avatar, improves message --- package-lock.json | 40 +++++++++ package.json | 1 + src/components/common/FlawState.tsx | 20 +---- src/components/common/Severity.tsx | 2 +- src/components/navigation/UserNav.tsx | 15 ++-- src/components/risk-assessment/FormatDate.tsx | 1 + .../risk-assessment/RiskAssessmentFeed.tsx | 86 +++++++++++++++---- src/components/ui/avatar.tsx | 48 +++++++++++ src/components/ui/badge.tsx | 14 +-- src/decorators/withOrganization.ts | 53 ++++++++++++ src/decorators/{withOrg.ts => withOrgs.ts} | 2 +- src/hooks/useActiveOrg.ts | 9 +- src/hooks/useCurrentUser.tsx | 20 +++++ src/pages/[organizationSlug]/billing.tsx | 6 +- src/pages/[organizationSlug]/index.tsx | 29 ++++++- src/pages/[organizationSlug]/projects.tsx | 26 +++++- .../projects/[projectSlug]/assets.tsx | 21 +++-- .../assets/[assetSlug]/dependency-graph.tsx | 8 +- .../[assetSlug]/flaws/[flawId]/index.tsx | 57 +++++++++--- .../assets/[assetSlug]/index.tsx | 6 +- .../assets/[assetSlug]/risk-handling.tsx | 6 +- .../[assetSlug]/risk-identification.tsx | 6 +- .../assets/[assetSlug]/sbom.json.tsx | 16 +--- .../assets/[assetSlug]/sbom.xml.tsx | 16 +--- .../assets/[assetSlug]/settings.tsx | 6 +- .../projects/[projectSlug]/index.tsx | 8 +- .../projects/[projectSlug]/settings.tsx | 8 +- src/pages/[organizationSlug]/settings.tsx | 26 +++++- src/pages/index.tsx | 6 +- src/pages/setup-organization.tsx | 8 +- src/pages/user-settings.tsx | 7 +- src/types/api/api.ts | 10 +++ src/types/auth.ts | 4 +- src/utils/view.ts | 44 ++++++++++ src/zustand/globalStore.ts | 11 ++- 35 files changed, 510 insertions(+), 136 deletions(-) create mode 100644 src/components/ui/avatar.tsx create mode 100644 src/decorators/withOrganization.ts rename src/decorators/{withOrg.ts => withOrgs.ts} (96%) create mode 100644 src/hooks/useCurrentUser.tsx create mode 100644 src/utils/view.ts diff --git a/package-lock.json b/package-lock.json index 048d9bd..562caf4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "@ory/client": "^1.4.0", "@ory/integrations": "^1.2.1", "@radix-ui/react-alert-dialog": "^1.1.1", + "@radix-ui/react-avatar": "^1.1.0", "@radix-ui/react-dialog": "^1.1.1", "@radix-ui/react-dropdown-menu": "^2.1.1", "@radix-ui/react-icons": "^1.3.0", @@ -1878,6 +1879,45 @@ } } }, + "node_modules/@radix-ui/react-avatar": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-avatar/-/react-avatar-1.1.0.tgz", + "integrity": "sha512-Q/PbuSMk/vyAd/UoIShVGZ7StHHeRFYU7wXmi5GV+8cLXflZAEpHL/F697H1klrzxKXNtZ97vWiC0q3RKUH8UA==", + "dependencies": { + "@radix-ui/react-context": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-avatar/node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz", + "integrity": "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-collection": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.0.tgz", diff --git a/package.json b/package.json index cb72e99..519f0cb 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "@ory/client": "^1.4.0", "@ory/integrations": "^1.2.1", "@radix-ui/react-alert-dialog": "^1.1.1", + "@radix-ui/react-avatar": "^1.1.0", "@radix-ui/react-dialog": "^1.1.1", "@radix-ui/react-dropdown-menu": "^2.1.1", "@radix-ui/react-icons": "^1.3.0", diff --git a/src/components/common/FlawState.tsx b/src/components/common/FlawState.tsx index e0f50c9..fa89aac 100644 --- a/src/components/common/FlawState.tsx +++ b/src/components/common/FlawState.tsx @@ -6,7 +6,7 @@ import { StopIcon, ClockIcon, BugAntIcon, -} from "@heroicons/react/24/solid"; +} from "@heroicons/react/24/outline"; import { FunctionComponent } from "react"; const FlawState: FunctionComponent<{ state: FlawDTO["state"] }> = ({ @@ -33,7 +33,7 @@ const FlawState: FunctionComponent<{ state: FlawDTO["state"] }> = ({
@@ -46,7 +46,7 @@ const FlawState: FunctionComponent<{ state: FlawDTO["state"] }> = ({
@@ -54,25 +54,13 @@ const FlawState: FunctionComponent<{ state: FlawDTO["state"] }> = ({
); - case "markedForMitigation": - return ( -
- - Marked for Mitigation -
- ); case "open": default: return (
diff --git a/src/components/common/Severity.tsx b/src/components/common/Severity.tsx index 14ae300..70e2d54 100644 --- a/src/components/common/Severity.tsx +++ b/src/components/common/Severity.tsx @@ -5,7 +5,7 @@ const getClassNames = (severity: string) => { case "CRITICAL": return "bg-red-200 text-red-700 border border-red-300"; case "HIGH": - return "bg-orange-200 text-orange-700 border border-red-300"; + return "text-orange-500 border border-orange-500"; case "MEDIUM": return "bg-yellow-300 border border-yellow-500 text-yellow-900"; case "LOW": diff --git a/src/components/navigation/UserNav.tsx b/src/components/navigation/UserNav.tsx index 916601f..61e71a5 100644 --- a/src/components/navigation/UserNav.tsx +++ b/src/components/navigation/UserNav.tsx @@ -34,6 +34,7 @@ import { DropdownMenuSeparator, DropdownMenuTrigger, } from "../ui/dropdown-menu"; +import { Avatar, AvatarFallback } from "../ui/avatar"; interface Props {} @@ -87,15 +88,17 @@ export default function UserNav() { - + + {/**/} + + {(user.traits.name.first ?? "").charAt(0).toUpperCase() + + (user.traits.name.last ?? "").charAt(0).toUpperCase()} + + )} diff --git a/src/components/risk-assessment/FormatDate.tsx b/src/components/risk-assessment/FormatDate.tsx index 0263ec2..725c72a 100644 --- a/src/components/risk-assessment/FormatDate.tsx +++ b/src/components/risk-assessment/FormatDate.tsx @@ -26,6 +26,7 @@ const timeAgo = (prevDate: Date) => { switch (true) { case diff < minute: const seconds = Math.round(diff / 1000); + if (seconds <= 10) return "Now"; return `${seconds} ${seconds > 1 ? "seconds" : "second"} ago`; case diff < hour: return Math.round(diff / minute) + " minutes ago"; diff --git a/src/components/risk-assessment/RiskAssessmentFeed.tsx b/src/components/risk-assessment/RiskAssessmentFeed.tsx index aec21f7..8e02df6 100644 --- a/src/components/risk-assessment/RiskAssessmentFeed.tsx +++ b/src/components/risk-assessment/RiskAssessmentFeed.tsx @@ -13,17 +13,23 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . +import { useActiveOrg } from "@/hooks/useActiveOrg"; +import { useCurrentUser } from "@/hooks/useCurrentUser"; import { FlawEventDTO } from "@/types/api/api"; -import FormatDate from "./FormatDate"; +import { getUsername } from "@/utils/view"; import { ArrowPathIcon, ArrowRightStartOnRectangleIcon, CheckIcon, MagnifyingGlassIcon, SpeakerXMarkIcon, + StopIcon, WrenchIcon, XMarkIcon, } from "@heroicons/react/24/outline"; +import { Avatar, AvatarFallback } from "../ui/avatar"; +import FormatDate from "./FormatDate"; +import { Badge } from "../ui/badge"; function EventTypeIcon({ eventType }: { eventType: FlawEventDTO["type"] }) { switch (eventType) { @@ -34,7 +40,7 @@ function EventTypeIcon({ eventType }: { eventType: FlawEventDTO["type"] }) { case "detected": return ; case "falsePositive": - return ; + return ; case "markedForMitigation": return ; case "markedForTransfer": @@ -44,6 +50,31 @@ function EventTypeIcon({ eventType }: { eventType: FlawEventDTO["type"] }) { } } +const eventTypeMessages = (type: FlawEventDTO["type"], flawName: string) => { + switch (type) { + case "accepted": + return "accepted the risk of " + flawName; + case "fixed": + return "fixed " + flawName; + case "detected": + return "detected " + flawName; + case "falsePositive": + return "marked " + flawName + " as false positive "; + case "rawRiskAssessmentUpdated": + return "updated the risk assessment of " + flawName + " automatically"; + } + return ""; +}; + +const maybeAddDot = (str: string) => { + if (!str) return ""; + + // check if string ends with a dot, exclamtion mark or question mark + if (str.match(/(\.|!|\?)$/)) return str; + + return str + "."; +}; + export default function RiskAssessmentFeed({ events, flawName, @@ -51,9 +82,14 @@ export default function RiskAssessmentFeed({ events: FlawEventDTO[]; flawName: string; }) { + const org = useActiveOrg(); + const currentUser = useCurrentUser(); return (
-
    +
      {events.map((event, index) => (
    • -

      - {event.userId} {event.type} {flawName} -

      -

      {event.justification}

      - {event.type === "rawRiskAssessmentUpdated" && ( -

      - Risk Assessment changed from{" "} - {event.arbitraryJsonData.oldRiskAssessment} to{" "} - {event.arbitraryJsonData.newRiskAssessment} -

      - )} +
      + {event.userId !== "system" && ( + + + {getUsername( + event.userId, + org, + currentUser, + ).realName.charAt(0)} + + + )} +
      +

      + {getUsername(event.userId, org, currentUser).displayName}{" "} + {eventTypeMessages(event.type, flawName)} +

      +

      + {maybeAddDot(event.justification)} + + {event.type === "rawRiskAssessmentUpdated" && ( + <> + {" "} + Risk Assessment changed from{" "} + {event.arbitraryJsonData.oldRiskAssessment} to{" "} + {event.arbitraryJsonData.newRiskAssessment} + + )} +

      +
      +
      -
      +
      diff --git a/src/components/ui/avatar.tsx b/src/components/ui/avatar.tsx new file mode 100644 index 0000000..7c718ff --- /dev/null +++ b/src/components/ui/avatar.tsx @@ -0,0 +1,48 @@ +import * as React from "react"; +import * as AvatarPrimitive from "@radix-ui/react-avatar"; + +import { cn } from "@/lib/utils"; + +const Avatar = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +Avatar.displayName = AvatarPrimitive.Root.displayName; + +const AvatarImage = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AvatarImage.displayName = AvatarPrimitive.Image.displayName; + +const AvatarFallback = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName; + +export { Avatar, AvatarImage, AvatarFallback }; diff --git a/src/components/ui/badge.tsx b/src/components/ui/badge.tsx index f000e3e..d3d5d60 100644 --- a/src/components/ui/badge.tsx +++ b/src/components/ui/badge.tsx @@ -1,7 +1,7 @@ -import * as React from "react" -import { cva, type VariantProps } from "class-variance-authority" +import * as React from "react"; +import { cva, type VariantProps } from "class-variance-authority"; -import { cn } from "@/lib/utils" +import { cn } from "@/lib/utils"; const badgeVariants = cva( "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", @@ -20,8 +20,8 @@ const badgeVariants = cva( defaultVariants: { variant: "default", }, - } -) + }, +); export interface BadgeProps extends React.HTMLAttributes, @@ -30,7 +30,7 @@ export interface BadgeProps function Badge({ className, variant, ...props }: BadgeProps) { return (
      - ) + ); } -export { Badge, badgeVariants } +export { Badge, badgeVariants }; diff --git a/src/decorators/withOrganization.ts b/src/decorators/withOrganization.ts new file mode 100644 index 0000000..f6c53ce --- /dev/null +++ b/src/decorators/withOrganization.ts @@ -0,0 +1,53 @@ +// Copyright (C) 2023 Tim Bastin, l3montree UG (haftungsbeschränkt) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import { OrganizationDetailsDTO } from "@/types/api/api"; +import { GetServerSidePropsContext } from "next"; +import { getApiClientFromContext } from "../services/devGuardApi"; +import { HttpError } from "./middleware"; + +export async function withOrganization(ctx: GetServerSidePropsContext) { + // get the devGuardApiClient + const devGuardApiClient = getApiClientFromContext(ctx); + // check if there is a slug in the query + const organizationSlug = ctx.params?.organizationSlug; + + if (organizationSlug) { + // get the organization + const r = await devGuardApiClient("/organizations/" + organizationSlug); + // parse the organization + const organization: OrganizationDetailsDTO = await r.json(); + + if (!r.ok) { + // it must be an 500 + throw new HttpError({ + redirect: { + destination: "/login", + permanent: false, + }, + }); + } + + return organization; + } else { + // it must be an 500 + throw new HttpError({ + redirect: { + destination: "/login", + permanent: false, + }, + }); + } +} diff --git a/src/decorators/withOrg.ts b/src/decorators/withOrgs.ts similarity index 96% rename from src/decorators/withOrg.ts rename to src/decorators/withOrgs.ts index e67699e..e32788b 100644 --- a/src/decorators/withOrg.ts +++ b/src/decorators/withOrgs.ts @@ -18,7 +18,7 @@ import { getApiClientFromContext } from "../services/devGuardApi"; import { HttpError } from "./middleware"; import { OrganizationDTO } from "@/types/api/api"; -export async function withOrg(ctx: GetServerSidePropsContext) { +export async function withOrgs(ctx: GetServerSidePropsContext) { // get the devGuardApiClient const devGuardApiClient = getApiClientFromContext(ctx); diff --git a/src/hooks/useActiveOrg.ts b/src/hooks/useActiveOrg.ts index c44f097..6afe28e 100644 --- a/src/hooks/useActiveOrg.ts +++ b/src/hooks/useActiveOrg.ts @@ -1,13 +1,10 @@ -import { useRouter } from "next/router"; +import { OrganizationDetailsDTO } from "@/types/api/api"; import { useStore } from "../zustand/globalStoreProvider"; -import { OrganizationDTO } from "@/types/api/api"; - -export function useActiveOrg(): OrganizationDTO { - const slug = useRouter().query.organizationSlug as string; +export function useActiveOrg(): OrganizationDetailsDTO { return useStore((s) => { // should only be used in organization slug pages // an organization cant be undefined in this subrouter - see the applied middleware - return s.organizations.find((o) => o.slug === slug)!; + return s.organization; }); } diff --git a/src/hooks/useCurrentUser.tsx b/src/hooks/useCurrentUser.tsx new file mode 100644 index 0000000..4fc8861 --- /dev/null +++ b/src/hooks/useCurrentUser.tsx @@ -0,0 +1,20 @@ +// Copyright (C) 2024 Tim Bastin, l3montree UG (haftungsbeschränkt) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import { useStore } from "@/zustand/globalStoreProvider"; + +export const useCurrentUser = () => { + return useStore((s) => s.session.identity); +}; diff --git a/src/pages/[organizationSlug]/billing.tsx b/src/pages/[organizationSlug]/billing.tsx index dff4036..eeb9595 100644 --- a/src/pages/[organizationSlug]/billing.tsx +++ b/src/pages/[organizationSlug]/billing.tsx @@ -11,8 +11,9 @@ import { config as appConfig } from "@/config"; import { useActiveOrg } from "@/hooks/useActiveOrg"; import { ProductsData } from "@/types/api/billing"; import { middleware } from "@/decorators/middleware"; -import { withOrg } from "@/decorators/withOrg"; +import { withOrgs } from "@/decorators/withOrgs"; import Link from "next/link"; +import { withOrganization } from "@/decorators/withOrganization"; export default function Billing({ productsData, @@ -137,6 +138,7 @@ export const getServerSideProps: GetServerSideProps = middleware( }, { session: withSession, - organizations: withOrg, + organizations: withOrgs, + organization: withOrganization, }, ); diff --git a/src/pages/[organizationSlug]/index.tsx b/src/pages/[organizationSlug]/index.tsx index 0c5ba4c..ef35431 100644 --- a/src/pages/[organizationSlug]/index.tsx +++ b/src/pages/[organizationSlug]/index.tsx @@ -17,18 +17,40 @@ import { middleware } from "@/decorators/middleware"; import { GetServerSidePropsContext } from "next"; import { FunctionComponent } from "react"; import Page from "../../components/Page"; -import { withOrg } from "../../decorators/withOrg"; +import { withOrgs } from "../../decorators/withOrgs"; import { withSession } from "../../decorators/withSession"; import { useActiveOrg } from "../../hooks/useActiveOrg"; import { useOrganizationMenu } from "@/hooks/useOrganizationMenu"; +import Link from "next/link"; +import { Badge } from "@/components/ui/badge"; +import { withOrganization } from "@/decorators/withOrganization"; const Home: FunctionComponent = () => { const activeOrg = useActiveOrg(); const orgMenu = useOrganizationMenu(); - return ; + return ( + + {activeOrg.name}{" "} + + Organization + + + } + title={activeOrg.name ?? "Loading..."} + Menu={orgMenu} + > + ); }; export default Home; @@ -41,6 +63,7 @@ export const getServerSideProps = middleware( }, { session: withSession, - organizations: withOrg, + organizations: withOrgs, + organization: withOrganization, }, ); diff --git a/src/pages/[organizationSlug]/projects.tsx b/src/pages/[organizationSlug]/projects.tsx index 7b1d483..16010ba 100644 --- a/src/pages/[organizationSlug]/projects.tsx +++ b/src/pages/[organizationSlug]/projects.tsx @@ -23,7 +23,7 @@ import { useForm } from "react-hook-form"; import { z } from "zod"; import Page from "../../components/Page"; -import { withOrg } from "../../decorators/withOrg"; +import { withOrgs } from "../../decorators/withOrgs"; import { withSession } from "../../decorators/withSession"; import { useActiveOrg } from "../../hooks/useActiveOrg"; import { @@ -66,6 +66,8 @@ import { DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import EmptyList from "@/components/common/EmptyList"; +import { Badge } from "@/components/ui/badge"; +import { withOrganization } from "@/decorators/withOrganization"; interface Props { projects: Array; @@ -107,7 +109,24 @@ const Home: FunctionComponent = ({ projects }) => { const orgMenu = useOrganizationMenu(); return ( - + + {activeOrg.name}{" "} + + Organization + + + } + title={activeOrg.name ?? "Loading..."} + Menu={orgMenu} + > @@ -238,6 +257,7 @@ export const getServerSideProps = middleware( }, { session: withSession, - organizations: withOrg, + organizations: withOrgs, + organization: withOrganization, }, ); diff --git a/src/pages/[organizationSlug]/projects/[projectSlug]/assets.tsx b/src/pages/[organizationSlug]/projects/[projectSlug]/assets.tsx index 3ffe3ad..1fe64a5 100644 --- a/src/pages/[organizationSlug]/projects/[projectSlug]/assets.tsx +++ b/src/pages/[organizationSlug]/projects/[projectSlug]/assets.tsx @@ -22,20 +22,25 @@ import { zodResolver } from "@hookform/resolvers/zod"; import EmptyList from "@/components/common/EmptyList"; import Section from "@/components/common/Section"; +import { Badge } from "@/components/ui/badge"; import { useProjectMenu } from "@/hooks/useProjectMenu"; import Link from "next/link"; import { toast } from "sonner"; import z from "zod"; -import { withOrg } from "../../../../decorators/withOrg"; +import { withOrgs } from "../../../../decorators/withOrgs"; import { withSession } from "../../../../decorators/withSession"; import { useActiveOrg } from "../../../../hooks/useActiveOrg"; import { browserApiClient, getApiClientFromContext, } from "../../../../services/devGuardApi"; -import { AssetDTO, EnvDTO, ProjectDTO } from "../../../../types/api/api"; -import { CreateAssetReq } from "../../../../types/api/req"; -import { Badge } from "@/components/ui/badge"; +import { + AssetDTO, + EnvDTO, + ProjectDTO, + RequirementsLevel, +} from "../../../../types/api/api"; +import { withOrganization } from "@/decorators/withOrganization"; interface Props { project: ProjectDTO & { @@ -61,6 +66,11 @@ const Index: FunctionComponent = ({ project }) => { const activeOrg = useActiveOrg(); const form = useForm({ resolver: zodResolver(formSchema), + defaultValues: { + confidentialityRequirement: RequirementsLevel.Medium, + integrityRequirement: RequirementsLevel.Medium, + availabilityRequirement: RequirementsLevel.Medium, + }, }); const projectMenu = useProjectMenu(); @@ -209,7 +219,8 @@ export const getServerSideProps = middleware( }, { session: withSession, - organizations: withOrg, + organizations: withOrgs, + organization: withOrganization, }, ); diff --git a/src/pages/[organizationSlug]/projects/[projectSlug]/assets/[assetSlug]/dependency-graph.tsx b/src/pages/[organizationSlug]/projects/[projectSlug]/assets/[assetSlug]/dependency-graph.tsx index fe68e99..550a1dc 100644 --- a/src/pages/[organizationSlug]/projects/[projectSlug]/assets/[assetSlug]/dependency-graph.tsx +++ b/src/pages/[organizationSlug]/projects/[projectSlug]/assets/[assetSlug]/dependency-graph.tsx @@ -28,7 +28,8 @@ import { Switch } from "@/components/ui/switch"; import { HEADER_HEIGHT, SIDEBAR_WIDTH } from "@/const/viewConstants"; import { middleware } from "@/decorators/middleware"; import { withAsset } from "@/decorators/withAsset"; -import { withOrg } from "@/decorators/withOrg"; +import { withOrganization } from "@/decorators/withOrganization"; +import { withOrgs } from "@/decorators/withOrgs"; import { withProject } from "@/decorators/withProject"; import { withSession } from "@/decorators/withSession"; import { useActiveAsset } from "@/hooks/useActiveAsset"; @@ -150,7 +151,7 @@ const DependencyGraphPage: FunctionComponent<{
      - {graph.root.risk === 0 && ( + {graph.root.risk !== 0 && (