diff --git a/client/src/app/components/AIModelDetails/AIModelDetails.tsx b/client/src/app/components/AIModelDetails/AIModelDetails.tsx index 97128c8ce..d9eae0bc0 100644 --- a/client/src/app/components/AIModelDetails/AIModelDetails.tsx +++ b/client/src/app/components/AIModelDetails/AIModelDetails.tsx @@ -1,10 +1,9 @@ -import type React from "react"; +import React from "react"; import { + Alert, Button, - Card, - CardBody, - CardTitle, + Content, DescriptionList, DescriptionListDescription, DescriptionListGroup, @@ -12,12 +11,15 @@ import { Label, Stack, StackItem, + Tab, + Tabs, + TabTitleText, } from "@patternfly/react-core"; import ExternalLinkAltIcon from "@patternfly/react-icons/dist/esm/icons/external-link-alt-icon"; import type { SbomModel } from "@app/client"; -import { getModelProperties } from "./utils"; +import { getModelProperties, parseSafetyRisks } from "./utils"; export interface ExternalReference { type: string; @@ -40,21 +42,23 @@ interface IAIModelDetailsProps { } export const AIModelDetails: React.FC = ({ model }) => { + const [activeTabKey, setActiveTabKey] = React.useState("overview"); const props = getModelProperties(model.properties); const externalRefs = parseExternalReferences(props.external_references); + const safetyRisks = parseSafetyRisks(props.safetyRiskAssessment); return ( - - - - Identity & Purpose - - + setActiveTabKey(key as string)} + aria-label="AI model details tabs" + role="region" + > + Overview}> + + + {props.typeOfModel && ( Model type @@ -88,20 +92,41 @@ export const AIModelDetails: React.FC = ({ model }) => { )} - - - + + {safetyRisks.length > 0 && ( + + + + {safetyRisks.map((risk, index) => ( + + {risk.name} + {risk.mitigationStrategy && ( + + Mitigation: {risk.mitigationStrategy} + + )} + + ))} + + + + )} + {props.limitation && ( + + Limitations + {props.limitation} + + )} + + - - - SBOM Metadata - - + SBOM Metadata} + > + + + {props.bomFormat && ( Format @@ -135,15 +160,17 @@ export const AIModelDetails: React.FC = ({ model }) => { )} - - - + + + - {externalRefs.length > 0 && ( - - - External References - + References} + > + + {externalRefs.length > 0 && ( + {externalRefs.map((ref) => ( @@ -160,26 +187,25 @@ export const AIModelDetails: React.FC = ({ model }) => { ))} - - - - )} - - {props.downloadLocation && ( - - - - )} - + + )} + {props.downloadLocation && ( + + + + )} + + + ); }; diff --git a/client/src/app/components/AIModelDetails/utils.ts b/client/src/app/components/AIModelDetails/utils.ts index 2d1eb90b9..0535ecd1e 100644 --- a/client/src/app/components/AIModelDetails/utils.ts +++ b/client/src/app/components/AIModelDetails/utils.ts @@ -1,3 +1,8 @@ +export interface SafetyRisk { + name: string; + mitigationStrategy?: string; +} + export interface ModelProperties { version?: string; licenses?: string; @@ -10,7 +15,7 @@ export interface ModelProperties { downloadLocation?: string; external_references?: string; limitation?: string; - safetyRiskAssessment?: string; + safetyRiskAssessment?: string | SafetyRisk[]; } export const getModelProperties = (properties: unknown): ModelProperties => { @@ -19,3 +24,11 @@ export const getModelProperties = (properties: unknown): ModelProperties => { } return {}; }; + +export const parseSafetyRisks = ( + value?: string | SafetyRisk[], +): SafetyRisk[] => { + if (!value) return []; + if (Array.isArray(value)) return value; + return [{ name: value }]; +};