diff --git a/apps/console/src/pages/organizations/mitigations/MitigationNewPage.tsx b/apps/console/src/pages/organizations/mitigations/MitigationNewPage.tsx
new file mode 100644
index 000000000..75ed1045a
--- /dev/null
+++ b/apps/console/src/pages/organizations/mitigations/MitigationNewPage.tsx
@@ -0,0 +1,30 @@
+import { PageTemplateSkeleton } from "@/components/PageTemplate";
+import { Suspense } from "react";
+import { lazy } from "@probo/react-lazy";
+import { useLocation } from "react-router";
+import { ErrorBoundaryWithLocation } from "../ErrorBoundary";
+
+const MitigationNewView = lazy(() => import("./MitigationNewView"));
+
+export function MitigationNewViewSkeleton() {
+ return (
+
+
+
+ );
+}
+
+export function MitigationNewPage() {
+ const location = useLocation();
+
+ return (
+
}>
+
+
+
+
+ );
+}
diff --git a/apps/console/src/pages/organizations/mitigations/MitigationNewView.tsx b/apps/console/src/pages/organizations/mitigations/MitigationNewView.tsx
new file mode 100644
index 000000000..652539ea5
--- /dev/null
+++ b/apps/console/src/pages/organizations/mitigations/MitigationNewView.tsx
@@ -0,0 +1,206 @@
+import { useState } from "react";
+import { ConnectionHandler, graphql, useMutation } from "react-relay";
+import { useParams, useNavigate } from "react-router";
+import { PageTemplate } from "@/components/PageTemplate";
+import { useToast } from "@/hooks/use-toast";
+import { MitigationNewViewCreateMitigationMutation } from "./__generated__/MitigationNewViewCreateMitigationMutation.graphql";
+import { EditableField } from "@/components/EditableField";
+import { Button } from "@/components/ui/button";
+import { Card } from "@/components/ui/card";
+import { Label } from "@/components/ui/label";
+import { cn } from "@/lib/utils";
+
+const createMitigationMutation = graphql`
+ mutation MitigationNewViewCreateMitigationMutation(
+ $input: CreateMitigationInput!
+ $connections: [ID!]!
+ ) {
+ createMitigation(input: $input) {
+ mitigationEdge @prependEdge(connections: $connections) {
+ node {
+ id
+ name
+ description
+ }
+ }
+ }
+ }
+`;
+
+export default function MitigationNewView() {
+ const { organizationId } = useParams<{
+ organizationId: string;
+ }>();
+ const navigate = useNavigate();
+ const { toast } = useToast();
+ const [formData, setFormData] = useState({
+ name: "",
+ description: "",
+ category: "",
+ importance: "EMPLOYEE" as "MANDATORY" | "PREFERRED" | "ADVANCED",
+ });
+
+ const [commit, isInFlight] =
+ useMutation
(
+ createMitigationMutation
+ );
+
+ const handleFieldChange = (field: keyof typeof formData, value: unknown) => {
+ setFormData((prev) => ({
+ ...prev,
+ [field]: value,
+ }));
+ };
+
+ const handleSubmit = (e: React.FormEvent) => {
+ e.preventDefault();
+
+ if (!formData.name || !formData.description) {
+ toast({
+ title: "Validation Error",
+ description: "Please fill in all required fields.",
+ variant: "destructive",
+ });
+ return;
+ }
+
+ const connectionId = ConnectionHandler.getConnectionID(
+ organizationId!,
+ "MitigationListView_mitigations"
+ );
+
+ commit({
+ variables: {
+ input: {
+ organizationId: organizationId!,
+ name: formData.name,
+ description: formData.description,
+ category: formData.category,
+ importance: formData.importance,
+ },
+ connections: [connectionId],
+ },
+ onCompleted(data, errors) {
+ if (errors) {
+ toast({
+ title: "Error",
+ description: errors[0]?.message || "Failed to create mitigation",
+ variant: "destructive",
+ });
+ return;
+ }
+
+ toast({
+ title: "Success",
+ description: "Mitigation created successfully",
+ });
+
+ navigate(
+ `/organizations/${organizationId}/mitigations/${data.createMitigation.mitigationEdge.node.id}`
+ );
+ },
+ onError(error) {
+ toast({
+ title: "Error",
+ description: error.message || "Failed to create framework",
+ variant: "destructive",
+ });
+ },
+ });
+ };
+
+ return (
+
+
+
+
+
+ );
+}
diff --git a/apps/console/src/pages/organizations/mitigations/__generated__/MitigationListViewQuery.graphql.ts b/apps/console/src/pages/organizations/mitigations/__generated__/MitigationListViewQuery.graphql.ts
index dd56acbef..e1106f8fc 100644
--- a/apps/console/src/pages/organizations/mitigations/__generated__/MitigationListViewQuery.graphql.ts
+++ b/apps/console/src/pages/organizations/mitigations/__generated__/MitigationListViewQuery.graphql.ts
@@ -1,5 +1,5 @@
/**
- * @generated SignedSource<>
+ * @generated SignedSource<>
* @lightSyntaxTransform
* @nogrep
*/
@@ -65,109 +65,135 @@ v3 = {
"storageKey": null
},
v4 = {
- "kind": "InlineFragment",
- "selections": [
- {
- "alias": null,
- "args": [
- {
- "kind": "Variable",
- "name": "first",
- "variableName": "first"
- },
- {
- "kind": "Literal",
- "name": "orderBy",
- "value": {
- "direction": "ASC",
- "field": "CREATED_AT"
- }
- }
- ],
- "concreteType": "MitigationConnection",
- "kind": "LinkedField",
- "name": "mitigations",
- "plural": false,
- "selections": [
- {
- "alias": null,
- "args": null,
- "concreteType": "MitigationEdge",
- "kind": "LinkedField",
- "name": "edges",
- "plural": true,
- "selections": [
- {
- "alias": null,
- "args": null,
- "concreteType": "Mitigation",
- "kind": "LinkedField",
- "name": "node",
- "plural": false,
- "selections": [
- (v3/*: 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": "state",
- "storageKey": null
- },
- {
- "alias": null,
- "args": null,
- "kind": "ScalarField",
- "name": "importance",
- "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
- }
- ],
- "storageKey": null
- }
- ],
- "type": "Organization",
- "abstractKey": null
-};
+ "kind": "Literal",
+ "name": "orderBy",
+ "value": {
+ "direction": "ASC",
+ "field": "CREATED_AT"
+ }
+},
+v5 = {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "__typename",
+ "storageKey": null
+},
+v6 = [
+ {
+ "alias": null,
+ "args": null,
+ "concreteType": "MitigationEdge",
+ "kind": "LinkedField",
+ "name": "edges",
+ "plural": true,
+ "selections": [
+ {
+ "alias": null,
+ "args": null,
+ "concreteType": "Mitigation",
+ "kind": "LinkedField",
+ "name": "node",
+ "plural": false,
+ "selections": [
+ (v3/*: 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": "state",
+ "storageKey": null
+ },
+ {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "importance",
+ "storageKey": null
+ },
+ {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "createdAt",
+ "storageKey": null
+ },
+ {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "updatedAt",
+ "storageKey": null
+ },
+ (v5/*: any*/)
+ ],
+ "storageKey": null
+ },
+ {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "cursor",
+ "storageKey": null
+ }
+ ],
+ "storageKey": null
+ },
+ {
+ "alias": null,
+ "args": null,
+ "concreteType": "PageInfo",
+ "kind": "LinkedField",
+ "name": "pageInfo",
+ "plural": false,
+ "selections": [
+ {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "endCursor",
+ "storageKey": null
+ },
+ {
+ "alias": null,
+ "args": null,
+ "kind": "ScalarField",
+ "name": "hasNextPage",
+ "storageKey": null
+ }
+ ],
+ "storageKey": null
+ }
+],
+v7 = [
+ {
+ "kind": "Variable",
+ "name": "first",
+ "variableName": "first"
+ },
+ (v4/*: any*/)
+];
return {
"fragment": {
"argumentDefinitions": [
@@ -187,7 +213,25 @@ return {
"plural": false,
"selections": [
(v3/*: any*/),
- (v4/*: any*/)
+ {
+ "kind": "InlineFragment",
+ "selections": [
+ {
+ "alias": "mitigations",
+ "args": [
+ (v4/*: any*/)
+ ],
+ "concreteType": "MitigationConnection",
+ "kind": "LinkedField",
+ "name": "__MitigationListView_mitigations_connection",
+ "plural": false,
+ "selections": (v6/*: any*/),
+ "storageKey": "__MitigationListView_mitigations_connection(orderBy:{\"direction\":\"ASC\",\"field\":\"CREATED_AT\"})"
+ }
+ ],
+ "type": "Organization",
+ "abstractKey": null
+ }
],
"storageKey": null
}
@@ -212,31 +256,64 @@ return {
"name": "node",
"plural": false,
"selections": [
- {
- "alias": null,
- "args": null,
- "kind": "ScalarField",
- "name": "__typename",
- "storageKey": null
- },
+ (v5/*: any*/),
(v3/*: any*/),
- (v4/*: any*/)
+ {
+ "kind": "InlineFragment",
+ "selections": [
+ {
+ "alias": null,
+ "args": (v7/*: any*/),
+ "concreteType": "MitigationConnection",
+ "kind": "LinkedField",
+ "name": "mitigations",
+ "plural": false,
+ "selections": (v6/*: any*/),
+ "storageKey": null
+ },
+ {
+ "alias": null,
+ "args": (v7/*: any*/),
+ "filters": [
+ "orderBy"
+ ],
+ "handle": "connection",
+ "key": "MitigationListView_mitigations",
+ "kind": "LinkedHandle",
+ "name": "mitigations"
+ }
+ ],
+ "type": "Organization",
+ "abstractKey": null
+ }
],
"storageKey": null
}
]
},
"params": {
- "cacheID": "112f47aec28111c799333f3cd144d3c1",
+ "cacheID": "cb49e5e7740ec71ce0b8e1abbdbcf24d",
"id": null,
- "metadata": {},
+ "metadata": {
+ "connection": [
+ {
+ "count": "first",
+ "cursor": null,
+ "direction": "forward",
+ "path": [
+ "organization",
+ "mitigations"
+ ]
+ }
+ ]
+ },
"name": "MitigationListViewQuery",
"operationKind": "query",
- "text": "query MitigationListViewQuery(\n $organizationId: ID!\n $first: Int\n) {\n organization: node(id: $organizationId) {\n __typename\n id\n ... on Organization {\n mitigations(first: $first, orderBy: {direction: ASC, field: CREATED_AT}) {\n edges {\n node {\n id\n name\n description\n category\n state\n importance\n createdAt\n updatedAt\n }\n }\n }\n }\n }\n}\n"
+ "text": "query MitigationListViewQuery(\n $organizationId: ID!\n $first: Int\n) {\n organization: node(id: $organizationId) {\n __typename\n id\n ... on Organization {\n mitigations(first: $first, orderBy: {direction: ASC, field: CREATED_AT}) {\n edges {\n node {\n id\n name\n description\n category\n state\n importance\n createdAt\n updatedAt\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n }\n }\n}\n"
}
};
})();
-(node as any).hash = "80e8c9cffc7d63bd3ce97ea90f696e82";
+(node as any).hash = "ee7fdf984d5cb376068ba53d0a56c59b";
export default node;
diff --git a/apps/console/src/pages/organizations/mitigations/__generated__/MitigationNewViewCreateMitigationMutation.graphql.ts b/apps/console/src/pages/organizations/mitigations/__generated__/MitigationNewViewCreateMitigationMutation.graphql.ts
new file mode 100644
index 000000000..a62b755da
--- /dev/null
+++ b/apps/console/src/pages/organizations/mitigations/__generated__/MitigationNewViewCreateMitigationMutation.graphql.ts
@@ -0,0 +1,179 @@
+/**
+ * @generated SignedSource<<56ffdc565a3b834423a8a704d0fa68dc>>
+ * @lightSyntaxTransform
+ * @nogrep
+ */
+
+/* tslint:disable */
+/* eslint-disable */
+// @ts-nocheck
+
+import { ConcreteRequest } from 'relay-runtime';
+export type MitigationImportance = "ADVANCED" | "MANDATORY" | "PREFERRED";
+export type CreateMitigationInput = {
+ category: string;
+ description: string;
+ importance: MitigationImportance;
+ name: string;
+ organizationId: string;
+};
+export type MitigationNewViewCreateMitigationMutation$variables = {
+ connections: ReadonlyArray;
+ input: CreateMitigationInput;
+};
+export type MitigationNewViewCreateMitigationMutation$data = {
+ readonly createMitigation: {
+ readonly mitigationEdge: {
+ readonly node: {
+ readonly description: string;
+ readonly id: string;
+ readonly name: string;
+ };
+ };
+ };
+};
+export type MitigationNewViewCreateMitigationMutation = {
+ response: MitigationNewViewCreateMitigationMutation$data;
+ variables: MitigationNewViewCreateMitigationMutation$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": "MitigationEdge",
+ "kind": "LinkedField",
+ "name": "mitigationEdge",
+ "plural": false,
+ "selections": [
+ {
+ "alias": null,
+ "args": null,
+ "concreteType": "Mitigation",
+ "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
+ }
+ ],
+ "storageKey": null
+ }
+ ],
+ "storageKey": null
+};
+return {
+ "fragment": {
+ "argumentDefinitions": [
+ (v0/*: any*/),
+ (v1/*: any*/)
+ ],
+ "kind": "Fragment",
+ "metadata": null,
+ "name": "MitigationNewViewCreateMitigationMutation",
+ "selections": [
+ {
+ "alias": null,
+ "args": (v2/*: any*/),
+ "concreteType": "CreateMitigationPayload",
+ "kind": "LinkedField",
+ "name": "createMitigation",
+ "plural": false,
+ "selections": [
+ (v3/*: any*/)
+ ],
+ "storageKey": null
+ }
+ ],
+ "type": "Mutation",
+ "abstractKey": null
+ },
+ "kind": "Request",
+ "operation": {
+ "argumentDefinitions": [
+ (v1/*: any*/),
+ (v0/*: any*/)
+ ],
+ "kind": "Operation",
+ "name": "MitigationNewViewCreateMitigationMutation",
+ "selections": [
+ {
+ "alias": null,
+ "args": (v2/*: any*/),
+ "concreteType": "CreateMitigationPayload",
+ "kind": "LinkedField",
+ "name": "createMitigation",
+ "plural": false,
+ "selections": [
+ (v3/*: any*/),
+ {
+ "alias": null,
+ "args": null,
+ "filters": null,
+ "handle": "prependEdge",
+ "key": "",
+ "kind": "LinkedHandle",
+ "name": "mitigationEdge",
+ "handleArgs": [
+ {
+ "kind": "Variable",
+ "name": "connections",
+ "variableName": "connections"
+ }
+ ]
+ }
+ ],
+ "storageKey": null
+ }
+ ]
+ },
+ "params": {
+ "cacheID": "3e7544674c48e16ec6698e04f8b5fb5e",
+ "id": null,
+ "metadata": {},
+ "name": "MitigationNewViewCreateMitigationMutation",
+ "operationKind": "mutation",
+ "text": "mutation MitigationNewViewCreateMitigationMutation(\n $input: CreateMitigationInput!\n) {\n createMitigation(input: $input) {\n mitigationEdge {\n node {\n id\n name\n description\n }\n }\n }\n}\n"
+ }
+};
+})();
+
+(node as any).hash = "32c38d7a04244e041fc6023efc010d7f";
+
+export default node;