setEditedRisk(null)}
/>
@@ -161,7 +141,7 @@ export default function RisksPage() {
>
| {risk.name} |
{risk.category} |
- {treatmentLabels[risk.treatment]} |
+ {getTreatment(__, risk.treatment)} |
>
- * @lightSyntaxTransform
- * @nogrep
- */
-
-/* tslint:disable */
-/* eslint-disable */
-// @ts-nocheck
-
-import { ConcreteRequest } from 'relay-runtime';
-export type RiskTreatment = "ACCEPTED" | "AVOIDED" | "MITIGATED" | "TRANSFERRED" | "%future added value";
-export type CreateRiskInput = {
- category: string;
- description: string;
- inherentImpact: number;
- inherentLikelihood: number;
- name: string;
- note?: string | null | undefined;
- organizationId: string;
- ownerId?: string | null | undefined;
- residualImpact?: number | null | undefined;
- residualLikelihood?: number | null | undefined;
- treatment: RiskTreatment;
-};
-export type NewRiskDialogMutation$variables = {
- connections: ReadonlyArray;
- input: CreateRiskInput;
-};
-export type NewRiskDialogMutation$data = {
- readonly createRisk: {
- readonly riskEdge: {
- readonly node: {
- readonly category: string;
- readonly createdAt: any;
- readonly description: string;
- readonly id: string;
- readonly inherentImpact: number;
- readonly inherentLikelihood: number;
- readonly name: string;
- readonly residualImpact: number;
- readonly residualLikelihood: number;
- readonly treatment: RiskTreatment;
- readonly updatedAt: any;
- };
- };
- };
-};
-export type NewRiskDialogMutation = {
- response: NewRiskDialogMutation$data;
- variables: NewRiskDialogMutation$variables;
-};
-
-const node: ConcreteRequest = (function(){
-var v0 = {
- "defaultValue": null,
- "kind": "LocalArgument",
- "name": "connections"
-},
-v1 = {
- "defaultValue": null,
- "kind": "LocalArgument",
- "name": "input"
-},
-v2 = [
- {
- "kind": "Variable",
- "name": "input",
- "variableName": "input"
- }
-],
-v3 = {
- "alias": null,
- "args": null,
- "concreteType": "RiskEdge",
- "kind": "LinkedField",
- "name": "riskEdge",
- "plural": false,
- "selections": [
- {
- "alias": null,
- "args": null,
- "concreteType": "Risk",
- "kind": "LinkedField",
- "name": "node",
- "plural": false,
- "selections": [
- {
- "alias": null,
- "args": null,
- "kind": "ScalarField",
- "name": "id",
- "storageKey": null
- },
- {
- "alias": null,
- "args": null,
- "kind": "ScalarField",
- "name": "name",
- "storageKey": null
- },
- {
- "alias": null,
- "args": null,
- "kind": "ScalarField",
- "name": "description",
- "storageKey": null
- },
- {
- "alias": null,
- "args": null,
- "kind": "ScalarField",
- "name": "category",
- "storageKey": null
- },
- {
- "alias": null,
- "args": null,
- "kind": "ScalarField",
- "name": "inherentLikelihood",
- "storageKey": null
- },
- {
- "alias": null,
- "args": null,
- "kind": "ScalarField",
- "name": "inherentImpact",
- "storageKey": null
- },
- {
- "alias": null,
- "args": null,
- "kind": "ScalarField",
- "name": "residualLikelihood",
- "storageKey": null
- },
- {
- "alias": null,
- "args": null,
- "kind": "ScalarField",
- "name": "residualImpact",
- "storageKey": null
- },
- {
- "alias": null,
- "args": null,
- "kind": "ScalarField",
- "name": "treatment",
- "storageKey": null
- },
- {
- "alias": null,
- "args": null,
- "kind": "ScalarField",
- "name": "createdAt",
- "storageKey": null
- },
- {
- "alias": null,
- "args": null,
- "kind": "ScalarField",
- "name": "updatedAt",
- "storageKey": null
- }
- ],
- "storageKey": null
- }
- ],
- "storageKey": null
-};
-return {
- "fragment": {
- "argumentDefinitions": [
- (v0/*: any*/),
- (v1/*: any*/)
- ],
- "kind": "Fragment",
- "metadata": null,
- "name": "NewRiskDialogMutation",
- "selections": [
- {
- "alias": null,
- "args": (v2/*: any*/),
- "concreteType": "CreateRiskPayload",
- "kind": "LinkedField",
- "name": "createRisk",
- "plural": false,
- "selections": [
- (v3/*: any*/)
- ],
- "storageKey": null
- }
- ],
- "type": "Mutation",
- "abstractKey": null
- },
- "kind": "Request",
- "operation": {
- "argumentDefinitions": [
- (v1/*: any*/),
- (v0/*: any*/)
- ],
- "kind": "Operation",
- "name": "NewRiskDialogMutation",
- "selections": [
- {
- "alias": null,
- "args": (v2/*: any*/),
- "concreteType": "CreateRiskPayload",
- "kind": "LinkedField",
- "name": "createRisk",
- "plural": false,
- "selections": [
- (v3/*: any*/),
- {
- "alias": null,
- "args": null,
- "filters": null,
- "handle": "prependEdge",
- "key": "",
- "kind": "LinkedHandle",
- "name": "riskEdge",
- "handleArgs": [
- {
- "kind": "Variable",
- "name": "connections",
- "variableName": "connections"
- }
- ]
- }
- ],
- "storageKey": null
- }
- ]
- },
- "params": {
- "cacheID": "e9b461ea2a98edd7d4a1b289d5d4673f",
- "id": null,
- "metadata": {},
- "name": "NewRiskDialogMutation",
- "operationKind": "mutation",
- "text": "mutation NewRiskDialogMutation(\n $input: CreateRiskInput!\n) {\n createRisk(input: $input) {\n riskEdge {\n node {\n id\n name\n description\n category\n inherentLikelihood\n inherentImpact\n residualLikelihood\n residualImpact\n treatment\n createdAt\n updatedAt\n }\n }\n }\n}\n"
- }
-};
-})();
-
-(node as any).hash = "532223f85627f715a9a457680073f358";
-
-export default node;
diff --git a/apps/console2/src/pages/organizations/risks/__generated__/RiskDetailPageQuery.graphql.ts b/apps/console2/src/pages/organizations/risks/__generated__/RiskDetailPageQuery.graphql.ts
new file mode 100644
index 000000000..554016736
--- /dev/null
+++ b/apps/console2/src/pages/organizations/risks/__generated__/RiskDetailPageQuery.graphql.ts
@@ -0,0 +1,262 @@
+/**
+ * @generated SignedSource<>
+ * @lightSyntaxTransform
+ * @nogrep
+ */
+
+/* tslint:disable */
+/* eslint-disable */
+// @ts-nocheck
+
+import { ConcreteRequest } from 'relay-runtime';
+import { FragmentRefs } from "relay-runtime";
+export type RiskTreatment = "ACCEPTED" | "AVOIDED" | "MITIGATED" | "TRANSFERRED" | "%future added value";
+export type RiskDetailPageQuery$variables = {
+ riskId: string;
+};
+export type RiskDetailPageQuery$data = {
+ readonly node: {
+ readonly createdAt?: any;
+ readonly description?: string;
+ readonly inherentImpact?: number;
+ readonly inherentLikelihood?: number;
+ readonly name?: string;
+ readonly note?: string;
+ readonly owner?: {
+ readonly fullName: string;
+ readonly id: string;
+ } | null | undefined;
+ readonly residualImpact?: number;
+ readonly residualLikelihood?: number;
+ readonly treatment?: RiskTreatment;
+ readonly updatedAt?: any;
+ readonly " $fragmentSpreads": FragmentRefs<"useRiskFormFragment">;
+ };
+};
+export type RiskDetailPageQuery = {
+ response: RiskDetailPageQuery$data;
+ variables: RiskDetailPageQuery$variables;
+};
+
+const node: ConcreteRequest = (function(){
+var v0 = [
+ {
+ "defaultValue": null,
+ "kind": "LocalArgument",
+ "name": "riskId"
+ }
+],
+v1 = [
+ {
+ "kind": "Variable",
+ "name": "id",
+ "variableName": "riskId"
+ }
+],
+v2 = {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "name",
+ "storageKey": null
+},
+v3 = {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "description",
+ "storageKey": null
+},
+v4 = {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "treatment",
+ "storageKey": null
+},
+v5 = {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "id",
+ "storageKey": null
+},
+v6 = {
+ "alias": null,
+ "args": null,
+ "concreteType": "People",
+ "kind": "LinkedField",
+ "name": "owner",
+ "plural": false,
+ "selections": [
+ (v5/*: any*/),
+ {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "fullName",
+ "storageKey": null
+ }
+ ],
+ "storageKey": null
+},
+v7 = {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "inherentLikelihood",
+ "storageKey": null
+},
+v8 = {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "inherentImpact",
+ "storageKey": null
+},
+v9 = {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "residualLikelihood",
+ "storageKey": null
+},
+v10 = {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "residualImpact",
+ "storageKey": null
+},
+v11 = {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "note",
+ "storageKey": null
+},
+v12 = {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "createdAt",
+ "storageKey": null
+},
+v13 = {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "updatedAt",
+ "storageKey": null
+};
+return {
+ "fragment": {
+ "argumentDefinitions": (v0/*: any*/),
+ "kind": "Fragment",
+ "metadata": null,
+ "name": "RiskDetailPageQuery",
+ "selections": [
+ {
+ "alias": null,
+ "args": (v1/*: any*/),
+ "concreteType": null,
+ "kind": "LinkedField",
+ "name": "node",
+ "plural": false,
+ "selections": [
+ {
+ "kind": "InlineFragment",
+ "selections": [
+ (v2/*: any*/),
+ (v3/*: any*/),
+ (v4/*: any*/),
+ (v6/*: any*/),
+ (v7/*: any*/),
+ (v8/*: any*/),
+ (v9/*: any*/),
+ (v10/*: any*/),
+ (v11/*: any*/),
+ (v12/*: any*/),
+ (v13/*: any*/),
+ {
+ "args": null,
+ "kind": "FragmentSpread",
+ "name": "useRiskFormFragment"
+ }
+ ],
+ "type": "Risk",
+ "abstractKey": null
+ }
+ ],
+ "storageKey": null
+ }
+ ],
+ "type": "Query",
+ "abstractKey": null
+ },
+ "kind": "Request",
+ "operation": {
+ "argumentDefinitions": (v0/*: any*/),
+ "kind": "Operation",
+ "name": "RiskDetailPageQuery",
+ "selections": [
+ {
+ "alias": null,
+ "args": (v1/*: any*/),
+ "concreteType": null,
+ "kind": "LinkedField",
+ "name": "node",
+ "plural": false,
+ "selections": [
+ {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "__typename",
+ "storageKey": null
+ },
+ (v5/*: any*/),
+ {
+ "kind": "InlineFragment",
+ "selections": [
+ (v2/*: any*/),
+ (v3/*: any*/),
+ (v4/*: any*/),
+ (v6/*: any*/),
+ (v7/*: any*/),
+ (v8/*: any*/),
+ (v9/*: any*/),
+ (v10/*: any*/),
+ (v11/*: any*/),
+ (v12/*: any*/),
+ (v13/*: any*/),
+ {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "category",
+ "storageKey": null
+ }
+ ],
+ "type": "Risk",
+ "abstractKey": null
+ }
+ ],
+ "storageKey": null
+ }
+ ]
+ },
+ "params": {
+ "cacheID": "0ed24be13c829da91705d506a46eb562",
+ "id": null,
+ "metadata": {},
+ "name": "RiskDetailPageQuery",
+ "operationKind": "query",
+ "text": "query RiskDetailPageQuery(\n $riskId: ID!\n) {\n node(id: $riskId) {\n __typename\n ... on Risk {\n name\n description\n treatment\n owner {\n id\n fullName\n }\n inherentLikelihood\n inherentImpact\n residualLikelihood\n residualImpact\n note\n createdAt\n updatedAt\n ...useRiskFormFragment\n }\n id\n }\n}\n\nfragment useRiskFormFragment on Risk {\n id\n name\n category\n description\n treatment\n inherentLikelihood\n inherentImpact\n residualLikelihood\n residualImpact\n note\n owner {\n id\n }\n}\n"
+ }
+};
+})();
+
+(node as any).hash = "94de027c557f9b633765f9dde7c0aae1";
+
+export default node;
diff --git a/apps/console2/src/pages/organizations/risks/__generated__/RisksPageUpdateRiskMutation.graphql.ts b/apps/console2/src/pages/organizations/risks/__generated__/RisksPageUpdateRiskMutation.graphql.ts
deleted file mode 100644
index af1bd5bcb..000000000
--- a/apps/console2/src/pages/organizations/risks/__generated__/RisksPageUpdateRiskMutation.graphql.ts
+++ /dev/null
@@ -1,220 +0,0 @@
-/**
- * @generated SignedSource<<78e5a6f1db510ffe28be99140539b234>>
- * @lightSyntaxTransform
- * @nogrep
- */
-
-/* tslint:disable */
-/* eslint-disable */
-// @ts-nocheck
-
-import { ConcreteRequest } from 'relay-runtime';
-export type RiskTreatment = "ACCEPTED" | "AVOIDED" | "MITIGATED" | "TRANSFERRED" | "%future added value";
-export type UpdateRiskInput = {
- category?: string | null | undefined;
- description?: string | null | undefined;
- id: string;
- inherentImpact?: number | null | undefined;
- inherentLikelihood?: number | null | undefined;
- name?: string | null | undefined;
- note?: string | null | undefined;
- ownerId?: string | null | undefined;
- residualImpact?: number | null | undefined;
- residualLikelihood?: number | null | undefined;
- treatment?: RiskTreatment | null | undefined;
-};
-export type RisksPageUpdateRiskMutation$variables = {
- input: UpdateRiskInput;
-};
-export type RisksPageUpdateRiskMutation$data = {
- readonly updateRisk: {
- readonly risk: {
- readonly category: string;
- readonly description: string;
- readonly id: string;
- readonly inherentImpact: number;
- readonly inherentLikelihood: number;
- readonly name: string;
- readonly note: string;
- readonly owner: {
- readonly fullName: string;
- readonly id: string;
- } | null | undefined;
- readonly residualImpact: number;
- readonly residualLikelihood: number;
- readonly treatment: RiskTreatment;
- readonly updatedAt: any;
- };
- };
-};
-export type RisksPageUpdateRiskMutation = {
- response: RisksPageUpdateRiskMutation$data;
- variables: RisksPageUpdateRiskMutation$variables;
-};
-
-const node: ConcreteRequest = (function(){
-var v0 = [
- {
- "defaultValue": null,
- "kind": "LocalArgument",
- "name": "input"
- }
-],
-v1 = {
- "alias": null,
- "args": null,
- "kind": "ScalarField",
- "name": "id",
- "storageKey": null
-},
-v2 = [
- {
- "alias": null,
- "args": [
- {
- "kind": "Variable",
- "name": "input",
- "variableName": "input"
- }
- ],
- "concreteType": "UpdateRiskPayload",
- "kind": "LinkedField",
- "name": "updateRisk",
- "plural": false,
- "selections": [
- {
- "alias": null,
- "args": null,
- "concreteType": "Risk",
- "kind": "LinkedField",
- "name": "risk",
- "plural": false,
- "selections": [
- (v1/*: any*/),
- {
- "alias": null,
- "args": null,
- "kind": "ScalarField",
- "name": "name",
- "storageKey": null
- },
- {
- "alias": null,
- "args": null,
- "kind": "ScalarField",
- "name": "description",
- "storageKey": null
- },
- {
- "alias": null,
- "args": null,
- "kind": "ScalarField",
- "name": "category",
- "storageKey": null
- },
- {
- "alias": null,
- "args": null,
- "kind": "ScalarField",
- "name": "inherentLikelihood",
- "storageKey": null
- },
- {
- "alias": null,
- "args": null,
- "kind": "ScalarField",
- "name": "inherentImpact",
- "storageKey": null
- },
- {
- "alias": null,
- "args": null,
- "kind": "ScalarField",
- "name": "residualLikelihood",
- "storageKey": null
- },
- {
- "alias": null,
- "args": null,
- "kind": "ScalarField",
- "name": "residualImpact",
- "storageKey": null
- },
- {
- "alias": null,
- "args": null,
- "kind": "ScalarField",
- "name": "treatment",
- "storageKey": null
- },
- {
- "alias": null,
- "args": null,
- "kind": "ScalarField",
- "name": "note",
- "storageKey": null
- },
- {
- "alias": null,
- "args": null,
- "kind": "ScalarField",
- "name": "updatedAt",
- "storageKey": null
- },
- {
- "alias": null,
- "args": null,
- "concreteType": "People",
- "kind": "LinkedField",
- "name": "owner",
- "plural": false,
- "selections": [
- (v1/*: any*/),
- {
- "alias": null,
- "args": null,
- "kind": "ScalarField",
- "name": "fullName",
- "storageKey": null
- }
- ],
- "storageKey": null
- }
- ],
- "storageKey": null
- }
- ],
- "storageKey": null
- }
-];
-return {
- "fragment": {
- "argumentDefinitions": (v0/*: any*/),
- "kind": "Fragment",
- "metadata": null,
- "name": "RisksPageUpdateRiskMutation",
- "selections": (v2/*: any*/),
- "type": "Mutation",
- "abstractKey": null
- },
- "kind": "Request",
- "operation": {
- "argumentDefinitions": (v0/*: any*/),
- "kind": "Operation",
- "name": "RisksPageUpdateRiskMutation",
- "selections": (v2/*: any*/)
- },
- "params": {
- "cacheID": "a3c421bf079967f96acf2215e72cf8a7",
- "id": null,
- "metadata": {},
- "name": "RisksPageUpdateRiskMutation",
- "operationKind": "mutation",
- "text": "mutation RisksPageUpdateRiskMutation(\n $input: UpdateRiskInput!\n) {\n updateRisk(input: $input) {\n risk {\n id\n name\n description\n category\n inherentLikelihood\n inherentImpact\n residualLikelihood\n residualImpact\n treatment\n note\n updatedAt\n owner {\n id\n fullName\n }\n }\n }\n}\n"
- }
-};
-})();
-
-(node as any).hash = "e3c8daa449e937742695adf8deb6dbe5";
-
-export default node;
diff --git a/apps/console2/src/routes.tsx b/apps/console2/src/routes.tsx
index 4386b94df..c2437e7df 100644
--- a/apps/console2/src/routes.tsx
+++ b/apps/console2/src/routes.tsx
@@ -7,7 +7,8 @@ import { MainLayout } from "./layouts/MainLayout";
import { AuthLayout, CenteredLayout, CenteredLayoutSkeleton } from "@probo/ui";
import { lazy, Suspense, type FC } from "react";
import { UnAuthenticatedError } from "./providers/RelayProviders";
-import { RisksPageSkeleton } from "./pages/organizations/risks/skeletons/RisksPage";
+import { RisksPageSkeleton } from "./components/skeletons/RisksPageSkeleton.tsx";
+import { PageSkeleton } from "./components/skeletons/PageSkeleton.tsx";
function ErrorBoundary() {
const error = useRouteError();
@@ -49,7 +50,7 @@ const routes = [
{
path: "organizations/new",
Component: lazy(
- () => import("./pages/organizations/NewOrganizationPage")
+ () => import("./pages/organizations/NewOrganizationPage"),
),
},
],
@@ -64,6 +65,13 @@ const routes = [
fallback: RisksPageSkeleton,
Component: lazy(() => import("./pages/organizations/risks/RisksPage")),
},
+ {
+ path: "risks/:riskId",
+ fallback: PageSkeleton,
+ Component: lazy(
+ () => import("./pages/organizations/risks/RiskDetailPage"),
+ ),
+ },
],
},
] satisfies Route[];
@@ -74,7 +82,6 @@ const routes = [
function routeTransformer(route: Route): RouteObject {
if ("fallback" in route && route.fallback) {
const fallback = ;
- console.log(fallback, route.path);
return {
...route,
children: route.children?.map(routeTransformer),
diff --git a/packages/helpers/src/index.ts b/packages/helpers/src/index.ts
index cb8880e62..aec95e965 100644
--- a/packages/helpers/src/index.ts
+++ b/packages/helpers/src/index.ts
@@ -1,5 +1,10 @@
export { objectKeys } from "./object";
export { sprintf } from "./string";
-export { getRiskImpacts, getRiskLikelihoods } from "./risk";
+export {
+ getTreatment,
+ getRiskImpacts,
+ getRiskLikelihoods,
+ getSeverity,
+} from "./risk";
export { times, groupBy } from "./array";
export { randomInt } from "./number";
diff --git a/packages/helpers/src/risk.ts b/packages/helpers/src/risk.ts
index e44428593..5eec1cc2c 100644
--- a/packages/helpers/src/risk.ts
+++ b/packages/helpers/src/risk.ts
@@ -25,6 +25,21 @@ export function getRiskImpacts(__: Translator) {
];
}
+export function getTreatment(__: Translator, treatment: string): string {
+ switch (treatment) {
+ case "MITIGATED":
+ return __("Mitigate");
+ case "ACCEPTED":
+ return __("Accept");
+ case "TRANSFERRED":
+ return __("Transfer");
+ case "AVOIDED":
+ return __("Avoid");
+ default:
+ return __("Unknown");
+ }
+}
+
export function getRiskLikelihoods(__: Translator) {
return [
{
@@ -49,3 +64,33 @@ export function getRiskLikelihoods(__: Translator) {
},
];
}
+
+function getRiskSeverities(__: Translator) {
+ return [
+ {
+ min: 15,
+ variant: "danger",
+ label: __("Critical"),
+ bg: "bg-danger",
+ color: "text-txt-danger",
+ },
+ {
+ min: 5,
+ variant: "warning",
+ label: __("High"),
+ bg: "bg-warning",
+ color: "text-txt-warning",
+ },
+ {
+ min: 0,
+ variant: "neutral",
+ label: __("Low"),
+ bg: "bg-txt-quaternary",
+ color: "text-txt-primary",
+ },
+ ] as const;
+}
+
+export function getSeverity(__: Translator, score: number) {
+ return getRiskSeverities(__).find((s) => score >= s.min);
+}
diff --git a/packages/ui/.storybook/preview.css b/packages/ui/.storybook/preview.css
index f4a6c20d1..a82a2d77f 100644
--- a/packages/ui/.storybook/preview.css
+++ b/packages/ui/.storybook/preview.css
@@ -2,3 +2,4 @@
@import "tailwindcss";
@import "tw-animate-css";
@import "../src/theme.css";
+@source "../../helpers/src";
diff --git a/packages/ui/src/Atoms/Badge/Badge.stories.tsx b/packages/ui/src/Atoms/Badge/Badge.stories.tsx
index 952cf0da5..377e8b74b 100644
--- a/packages/ui/src/Atoms/Badge/Badge.stories.tsx
+++ b/packages/ui/src/Atoms/Badge/Badge.stories.tsx
@@ -9,23 +9,34 @@ export default {
type Story = StoryObj;
+const sizes = ["sm", "md"] as const;
+const variants = [
+ "success",
+ "warning",
+ "danger",
+ "info",
+ "neutral",
+ "outline",
+ "highlight",
+] as const;
+
export const Default: Story = {
render: () => (
- <>
- {(
- [
- "success",
- "warning",
- "danger",
- "info",
- "neutral",
- "outline",
- ] as const
- ).map((variant) => (
-
- {variant}
-
+
+ {sizes.map((size) => (
+
+
+ Size : {size}
+
+
+ {variants.map((variant) => (
+
+ {variant}
+
+ ))}
+
+
))}
- >
+
),
};
diff --git a/packages/ui/src/Atoms/Badge/Badge.tsx b/packages/ui/src/Atoms/Badge/Badge.tsx
index fe219b62a..72909fb15 100644
--- a/packages/ui/src/Atoms/Badge/Badge.tsx
+++ b/packages/ui/src/Atoms/Badge/Badge.tsx
@@ -2,11 +2,19 @@ import type { HTMLAttributes } from "react";
import { tv } from "tailwind-variants";
type Props = {
- variant?: "success" | "warning" | "danger" | "info" | "neutral" | "outline";
+ variant?:
+ | "success"
+ | "warning"
+ | "danger"
+ | "info"
+ | "neutral"
+ | "outline"
+ | "highlight";
+ size?: "sm" | "md";
} & HTMLAttributes;
const badge = tv({
- base: "text-xs font-medium py-[2px] px-[6px] rounded-lg w-max",
+ base: "font-medium rounded-lg w-max flex gap-1 items-center",
variants: {
variant: {
success: "bg-success text-txt-success",
@@ -15,10 +23,18 @@ const badge = tv({
info: "bg-info text-txt-info",
neutral: "bg-subtle text-txt-secondary",
outline: "text-txt-tertiary border border-border-low",
+ highlight: "bg-highlight text-txt-primary",
},
+ size: {
+ sm: "text-xs py-[2px] px-[6px]",
+ md: "text-sm py-[6px] px-2",
+ },
+ },
+ defaultVariants: {
+ size: "md",
},
});
export function Badge(props: Props) {
- return ;
+ return ;
}
diff --git a/packages/ui/src/Atoms/Dropdown/Dropdown.tsx b/packages/ui/src/Atoms/Dropdown/Dropdown.tsx
index e7a7ca907..802286cc1 100644
--- a/packages/ui/src/Atoms/Dropdown/Dropdown.tsx
+++ b/packages/ui/src/Atoms/Dropdown/Dropdown.tsx
@@ -61,12 +61,19 @@ const dropdownItem = tv({
},
});
-export function ActionDropdown(props: Omit) {
+export function ActionDropdown(
+ props: Omit & {
+ variant?: ComponentProps["variant"];
+ },
+) {
return (
+
}
/>
);
diff --git a/packages/ui/src/Atoms/PropertyRow/PropertyRow.tsx b/packages/ui/src/Atoms/PropertyRow/PropertyRow.tsx
index 2d1884f63..266d8b98f 100644
--- a/packages/ui/src/Atoms/PropertyRow/PropertyRow.tsx
+++ b/packages/ui/src/Atoms/PropertyRow/PropertyRow.tsx
@@ -3,7 +3,7 @@ import { Label } from "../Label/Label";
type Props = {
label: string;
- id: string;
+ id?: string;
children: ReactNode;
error?: string;
};
diff --git a/packages/ui/src/Atoms/Select/Select.tsx b/packages/ui/src/Atoms/Select/Select.tsx
index 4da51bd9f..4a78044bb 100644
--- a/packages/ui/src/Atoms/Select/Select.tsx
+++ b/packages/ui/src/Atoms/Select/Select.tsx
@@ -112,7 +112,7 @@ export function Select({
(c) => c.props.value?.toString() === value?.toString(),
)
: undefined;
- console.log(childrenArr, valueNode);
+
return (
diff --git a/packages/ui/src/Layouts/Layout.stories.tsx b/packages/ui/src/Layouts/Layout.stories.tsx
index 97aa3dd45..021a21776 100644
--- a/packages/ui/src/Layouts/Layout.stories.tsx
+++ b/packages/ui/src/Layouts/Layout.stories.tsx
@@ -1,4 +1,4 @@
-import { Layout } from "./Layout";
+import { Layout, Drawer } from "./Layout";
import type { Meta, StoryObj } from "@storybook/react";
const meta = {
@@ -12,4 +12,36 @@ const meta = {
export default meta;
type Story = StoryObj;
-export const Default: Story = {};
+export const Default: Story = {
+ args: {
+ children: (
+
+ Lorem ipsum dolor sit amet consectetur, adipisicing elit.
+ Tempora tempore odit at ipsa dignissimos cumque deleniti, illum
+ rerum sunt laudantium molestias ducimus vero maiores eligendi
+ necessitatibus nemo animi. Ipsam, fugit.
+
+ ),
+ },
+};
+
+export const WithSidebar: Story = {
+ args: {
+ children: (
+ <>
+
+ Lorem ipsum dolor sit amet consectetur, adipisicing elit.
+ Tempora tempore odit at ipsa dignissimos cumque deleniti,
+ illum rerum sunt laudantium molestias ducimus vero maiores
+ eligendi necessitatibus nemo animi. Ipsam, fugit.
+
+
+ Lorem ipsum dolor sit amet consectetur adipisicing elit.
+ Nostrum reiciendis eveniet possimus illo rerum labore nisi
+ voluptatibus sequi consectetur corrupti, a, sunt dicta ut ad
+ pariatur. Beatae optio libero pariatur!
+
+ >
+ ),
+ },
+};
diff --git a/packages/ui/src/Layouts/Layout.tsx b/packages/ui/src/Layouts/Layout.tsx
index 75d52c0bd..8f6746577 100644
--- a/packages/ui/src/Layouts/Layout.tsx
+++ b/packages/ui/src/Layouts/Layout.tsx
@@ -1,39 +1,84 @@
-import type { PropsWithChildren, ReactNode } from "react";
+import {
+ createContext,
+ useContext,
+ useEffect,
+ useState,
+ type PropsWithChildren,
+ type ReactNode,
+ useMemo,
+} from "react";
import { Sidebar } from "../Atoms/Sidebar/Sidebar.tsx";
import { Logo } from "../Atoms/Logo/Logo.tsx";
import { Toasts } from "../Atoms/Toasts/Toasts.tsx";
+import { createPortal } from "react-dom";
+import clsx from "clsx";
type Props = PropsWithChildren<{
header: ReactNode;
sidebar: ReactNode;
}>;
+const LayoutContext = createContext({
+ setDrawer: (() => {}) as (v: boolean) => void,
+});
+
export function Layout({ header, sidebar, children }: Props) {
+ const [hasDrawer, setDrawer] = useState(false);
return (
-
-
-
- {sidebar}
-
-
- {children}
-
-
+ ({
+ setDrawer,
+ }),
+ [],
+ )}
+ >
+
+
+
+ {sidebar}
+
+
+ {children}
+
+
+
+
-
-
+
+ );
+}
+
+export function Drawer({ children }: PropsWithChildren) {
+ const { setDrawer } = useContext(LayoutContext);
+ useEffect(() => {
+ setDrawer(true);
+ return () => {
+ setDrawer(false);
+ };
+ }, []);
+ return createPortal(
+ ,
+ document.body,
);
}
diff --git a/packages/ui/src/Molecules/Badge/SeverityBadge.tsx b/packages/ui/src/Molecules/Badge/SeverityBadge.tsx
index 90b13c333..1b7d9ccc0 100644
--- a/packages/ui/src/Molecules/Badge/SeverityBadge.tsx
+++ b/packages/ui/src/Molecules/Badge/SeverityBadge.tsx
@@ -2,35 +2,29 @@ import { useTranslate } from "@probo/i18n";
import { Badge } from "../../Atoms/Badge/Badge.tsx";
type Props = {
- severity: number;
+ score: number;
};
-const badgeVariant = (severity: number) => {
- if (severity >= 75) {
+const badgeVariant = (score: number) => {
+ if (score >= 15) {
return "danger";
}
- if (severity >= 50) {
+ if (score >= 5) {
return "warning";
}
- if (severity >= 25) {
- return "info";
- }
- return "success";
+ return "neutral";
};
-export function SeverityBadge({ severity }: Props) {
+export function SeverityBadge({ score }: Props) {
const { __ } = useTranslate();
const label = () => {
- if (severity >= 75) {
+ if (score >= 15) {
return __("Critical");
}
- if (severity >= 50) {
+ if (score >= 5) {
return __("High");
}
- if (severity >= 25) {
- return __("Low");
- }
- return __("None");
+ return __("Negligible");
};
- return {label()};
+ return {label()};
}
diff --git a/packages/ui/src/Molecules/PageHeader/PageHeader.tsx b/packages/ui/src/Molecules/PageHeader/PageHeader.tsx
index 88484aca7..b04036552 100644
--- a/packages/ui/src/Molecules/PageHeader/PageHeader.tsx
+++ b/packages/ui/src/Molecules/PageHeader/PageHeader.tsx
@@ -1,7 +1,7 @@
-import type { PropsWithChildren } from "react";
+import type { PropsWithChildren, ReactNode } from "react";
type Props = PropsWithChildren<{
- title: string;
+ title: ReactNode;
description?: string;
}>;
diff --git a/packages/ui/src/Molecules/Risks/RiskOverview.stories.tsx b/packages/ui/src/Molecules/Risks/RiskOverview.stories.tsx
new file mode 100644
index 000000000..25309fdd7
--- /dev/null
+++ b/packages/ui/src/Molecules/Risks/RiskOverview.stories.tsx
@@ -0,0 +1,22 @@
+import { RiskOverview } from "./RiskOverview";
+import type { Meta, StoryObj } from "@storybook/react";
+
+export default {
+ title: "Molecules/Risks/RiskOverview",
+ component: RiskOverview,
+ argTypes: {},
+} satisfies Meta ;
+
+type Story = StoryObj;
+
+export const Default: Story = {
+ args: {
+ type: "inherent",
+ risk: {
+ inherentLikelihood: 5,
+ inherentImpact: 5,
+ residualLikelihood: 1,
+ residualImpact: 1,
+ },
+ },
+};
diff --git a/packages/ui/src/Molecules/Risks/RiskOverview.tsx b/packages/ui/src/Molecules/Risks/RiskOverview.tsx
new file mode 100644
index 000000000..c14f6fdda
--- /dev/null
+++ b/packages/ui/src/Molecules/Risks/RiskOverview.tsx
@@ -0,0 +1,88 @@
+import { useTranslate } from "@probo/i18n";
+import { Card } from "../../Atoms/Card/Card";
+import { levelColors } from "./constants";
+import {
+ getRiskImpacts,
+ getRiskLikelihoods,
+ getSeverity,
+} from "@probo/helpers";
+import clsx from "clsx";
+
+type Props = {
+ type: "inherent" | "residual";
+ risk?: Risk;
+};
+
+type Risk = {
+ inherentLikelihood: number;
+ inherentImpact: number;
+ residualLikelihood: number;
+ residualImpact: number;
+};
+
+const getColor = (score: number): string => {
+ return levelColors[Math.ceil(score / 2) - 1].color;
+};
+
+export function RiskOverview({ type, risk }: Props) {
+ const { __ } = useTranslate();
+ const impact = risk?.[`${type}Impact`] ?? 0;
+ const likelihood = risk?.[`${type}Likelihood`] ?? 0;
+ const severity = getSeverity(__, impact * likelihood);
+ return (
+
+
+ {type === "inherent" ? __("Initial Risk") : __("Residual Risk")}
+
+
+
+
+
+
+ {__("Severity")}
+
+ {severity?.label}
+
+
+
+ );
+}
+
+function RiskOverviewBadge({
+ score,
+ label,
+ textCb,
+}: {
+ score: number;
+ label: string;
+ textCb: (t: (s: string) => string) => { value: number; label: string }[];
+}) {
+ const { __ } = useTranslate();
+ return (
+
+ {__(label)}
+
+ {textCb(__).find((i) => i.value === score)?.label} ({score})
+
+
+ );
+}
diff --git a/packages/ui/src/Molecules/RisksChart/RisksChart.stories.tsx b/packages/ui/src/Molecules/Risks/RisksChart.stories.tsx
similarity index 95%
rename from packages/ui/src/Molecules/RisksChart/RisksChart.stories.tsx
rename to packages/ui/src/Molecules/Risks/RisksChart.stories.tsx
index fa8a66666..883642ac7 100644
--- a/packages/ui/src/Molecules/RisksChart/RisksChart.stories.tsx
+++ b/packages/ui/src/Molecules/Risks/RisksChart.stories.tsx
@@ -3,7 +3,7 @@ import { RisksChart } from "./RisksChart";
import type { Meta, StoryObj } from "@storybook/react";
export default {
- title: "Molecules/RisksChart",
+ title: "Molecules/Risks/RisksChart",
component: RisksChart,
argTypes: {},
} satisfies Meta;
diff --git a/packages/ui/src/Molecules/RisksChart/RisksChart.tsx b/packages/ui/src/Molecules/Risks/RisksChart.tsx
similarity index 96%
rename from packages/ui/src/Molecules/RisksChart/RisksChart.tsx
rename to packages/ui/src/Molecules/Risks/RisksChart.tsx
index 6b7a2d5f9..36896198f 100644
--- a/packages/ui/src/Molecules/RisksChart/RisksChart.tsx
+++ b/packages/ui/src/Molecules/Risks/RisksChart.tsx
@@ -9,8 +9,8 @@ import {
DropdownSeparator,
} from "../../Atoms/Dropdown/Dropdown";
import { IconChevronRight, IconFire3 } from "../../Atoms/Icons";
-import { Button } from "../../Atoms/Button/Button";
import { Link } from "react-router";
+import { levelColors } from "./constants";
type Props = {
organizationId: string;
@@ -27,21 +27,6 @@ type Risk = {
residualImpact: number;
};
-const levelColors = [
- {
- color: "bg-txt-success",
- bg: "bg-success",
- },
- {
- color: "bg-txt-warning",
- bg: "bg-warning",
- },
- {
- color: "bg-txt-danger",
- bg: "bg-danger",
- },
-] as const;
-
const getLevel = (score: number): 0 | 1 | 2 => {
if (score >= 15) {
return 2;
diff --git a/packages/ui/src/Molecules/Risks/constants.ts b/packages/ui/src/Molecules/Risks/constants.ts
new file mode 100644
index 000000000..fde649def
--- /dev/null
+++ b/packages/ui/src/Molecules/Risks/constants.ts
@@ -0,0 +1,14 @@
+export const levelColors = [
+ {
+ color: "bg-txt-success",
+ bg: "bg-success",
+ },
+ {
+ color: "bg-txt-warning",
+ bg: "bg-warning",
+ },
+ {
+ color: "bg-txt-danger",
+ bg: "bg-danger",
+ },
+] as const;
diff --git a/packages/ui/src/index.ts b/packages/ui/src/index.ts
index 41439d103..70ab96933 100644
--- a/packages/ui/src/index.ts
+++ b/packages/ui/src/index.ts
@@ -1,5 +1,5 @@
// Layouts
-export { Layout } from "./Layouts/Layout";
+export { Layout, Drawer } from "./Layouts/Layout";
export { AuthLayout } from "./Layouts/AuthLayout";
export { ErrorLayout } from "./Layouts/ErrorLayout";
export {
@@ -14,6 +14,7 @@ export { SidebarItem } from "./Atoms/Sidebar/SidebarItem";
export { Button } from "./Atoms/Button/Button";
export { Card } from "./Atoms/Card/Card";
export { Breadcrumb } from "./Atoms/Breadcrumb/Breadcrumb";
+export { Badge } from "./Atoms/Badge/Badge";
export {
Dropdown,
ActionDropdown,
@@ -40,7 +41,8 @@ export { Dialog, DialogContent, DialogFooter } from "./Molecules/Dialog/Dialog";
export { RiskBadge } from "./Molecules/Badge/RiskBadge";
export { SeverityBadge } from "./Molecules/Badge/SeverityBadge.tsx";
export { ConfirmDialog } from "./Molecules/Dialog/ConfirmDialog.tsx";
-export { RisksChart } from "./Molecules/RisksChart/RisksChart";
+export { RisksChart } from "./Molecules/Risks/RisksChart";
+export { RiskOverview } from "./Molecules/Risks/RiskOverview";
// Hooks
export { useToast, Toasts } from "./Atoms/Toasts/Toasts";
diff --git a/packages/ui/src/theme.css b/packages/ui/src/theme.css
index 7a88ab6d2..498fa8e4d 100644
--- a/packages/ui/src/theme.css
+++ b/packages/ui/src/theme.css
@@ -13,7 +13,7 @@
--color-txt-invert: var(--color-invert);
--color-txt-secondary: #6b716a;
--color-txt-tertiary: #818780;
- --color-txt-quaternary: #d8dcd8;
+ --color-txt-quaternary: #c3c8c2;
--color-txt-disabled: #000000;
--color-txt-accent: #93c926;
--color-txt-danger: #cd2b31;
|