From 1fbde8649b11eaef47fc54f191786a69da11a257 Mon Sep 17 00:00:00 2001 From: gearnode Date: Mon, 17 Mar 2025 10:19:40 +0100 Subject: [PATCH] Allow to edit control state without going to edit page Signed-off-by: gearnode --- .../console/src/pages/ControlOverviewPage.tsx | 84 +++++++++++- .../ControlOverviewPageQuery.graphql.ts | 19 +-- ...wPageUpdateControlStateMutation.graphql.ts | 129 ++++++++++++++++++ 3 files changed, 218 insertions(+), 14 deletions(-) create mode 100644 apps/console/src/pages/__generated__/ControlOverviewPageUpdateControlStateMutation.graphql.ts diff --git a/apps/console/src/pages/ControlOverviewPage.tsx b/apps/console/src/pages/ControlOverviewPage.tsx index 75362bcef..cea9d0101 100644 --- a/apps/console/src/pages/ControlOverviewPage.tsx +++ b/apps/console/src/pages/ControlOverviewPage.tsx @@ -54,6 +54,12 @@ import { PopoverContent, PopoverTrigger, } from "@/components/ui/popover"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, +} from "@/components/ui/select"; import ReactMarkdown from "react-markdown"; import { Helmet } from "react-helmet-async"; @@ -66,6 +72,7 @@ import type { ControlOverviewPageDeleteEvidenceMutation as ControlOverviewPageDe import type { ControlOverviewPageAssignTaskMutation as ControlOverviewPageAssignTaskMutationType } from "./__generated__/ControlOverviewPageAssignTaskMutation.graphql"; import type { ControlOverviewPageUnassignTaskMutation as ControlOverviewPageUnassignTaskMutationType } from "./__generated__/ControlOverviewPageUnassignTaskMutation.graphql"; import type { ControlOverviewPageOrganizationQuery$data } from "./__generated__/ControlOverviewPageOrganizationQuery.graphql"; +import type { ControlOverviewPageUpdateControlStateMutation as ControlOverviewPageUpdateControlStateMutationType } from "./__generated__/ControlOverviewPageUpdateControlStateMutation.graphql"; import { Textarea } from "@/components/ui/textarea"; // Function to format ISO8601 duration to human-readable format @@ -115,6 +122,7 @@ const controlOverviewPageQuery = graphql` state importance category + version tasks(first: 100) @connection(key: "ControlOverviewPage_tasks") { __id edges { @@ -276,6 +284,20 @@ const unassignTaskMutation = graphql` } `; +const updateControlStateMutation = graphql` + mutation ControlOverviewPageUpdateControlStateMutation( + $input: UpdateControlInput! + ) { + updateControl(input: $input) { + control { + id + state + version + } + } + } +`; + const organizationQuery = graphql` query ControlOverviewPageOrganizationQuery($organizationId: ID!) { organization: node(id: $organizationId) { @@ -410,6 +432,11 @@ function ControlOverviewPageContent({ unassignTaskMutation ); + const [updateControlState] = + useMutation( + updateControlStateMutation + ); + const [isCreateTaskOpen, setIsCreateTaskOpen] = useState(false); const [newTaskName, setNewTaskName] = useState(""); const [newTaskDescription, setNewTaskDescription] = useState(""); @@ -965,6 +992,38 @@ function ControlOverviewPageContent({ }); }; + // Function to handle control state change + const handleControlStateChange = (newState: string) => { + updateControlState({ + variables: { + input: { + id: data.control.id, + state: newState as + | "NOT_STARTED" + | "IN_PROGRESS" + | "IMPLEMENTED" + | "NOT_APPLICABLE", + expectedVersion: data.control.version!, + }, + }, + onCompleted: () => { + toast({ + title: "Control state updated", + description: `Control state has been updated to ${formatState( + newState + )}.`, + }); + }, + onError: (error) => { + toast({ + title: "Error updating control state", + description: error.message, + variant: "destructive", + }); + }, + }); + }; + return ( <> @@ -978,13 +1037,26 @@ function ControlOverviewPageContent({ -
- {formatState(data.control.state)} -
+ +
+ {formatState(data.control.state)} +
+
+ + Not Started + In Progress + Implemented + Not Applicable + +
> + * @generated SignedSource<<84b34aa7e582728a8386d1fe1ef3fb58>> * @lightSyntaxTransform * @nogrep */ @@ -55,6 +55,7 @@ export type ControlOverviewPageQuery$data = { }; }>; }; + readonly version?: number; }; }; export type ControlOverviewPageQuery = { @@ -123,14 +124,14 @@ v8 = { "alias": null, "args": null, "kind": "ScalarField", - "name": "timeEstimate", + "name": "version", "storageKey": null }, v9 = { "alias": null, "args": null, "kind": "ScalarField", - "name": "version", + "name": "timeEstimate", "storageKey": null }, v10 = { @@ -306,6 +307,7 @@ return { (v5/*: any*/), (v6/*: any*/), (v7/*: any*/), + (v8/*: any*/), { "alias": "tasks", "args": null, @@ -334,8 +336,8 @@ return { (v3/*: any*/), (v4/*: any*/), (v5/*: any*/), - (v8/*: any*/), (v9/*: any*/), + (v8/*: any*/), (v10/*: any*/), { "alias": "evidences", @@ -395,6 +397,7 @@ return { (v5/*: any*/), (v6/*: any*/), (v7/*: any*/), + (v8/*: any*/), { "alias": null, "args": (v16/*: any*/), @@ -423,8 +426,8 @@ return { (v3/*: any*/), (v4/*: any*/), (v5/*: any*/), - (v8/*: any*/), (v9/*: any*/), + (v8/*: any*/), (v10/*: any*/), { "alias": null, @@ -477,7 +480,7 @@ return { ] }, "params": { - "cacheID": "1d26d59ea2232a213d01636e8d388919", + "cacheID": "4bcea497959829f998cdab71797c8675", "id": null, "metadata": { "connection": [ @@ -500,11 +503,11 @@ return { }, "name": "ControlOverviewPageQuery", "operationKind": "query", - "text": "query ControlOverviewPageQuery(\n $controlId: ID!\n) {\n control: node(id: $controlId) {\n __typename\n id\n ... on Control {\n name\n description\n state\n importance\n category\n tasks(first: 100) {\n edges {\n node {\n id\n name\n description\n state\n timeEstimate\n version\n assignedTo {\n id\n fullName\n primaryEmailAddress\n }\n evidences(first: 50) {\n edges {\n node {\n id\n mimeType\n filename\n size\n state\n createdAt\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n }\n }\n}\n" + "text": "query ControlOverviewPageQuery(\n $controlId: ID!\n) {\n control: node(id: $controlId) {\n __typename\n id\n ... on Control {\n name\n description\n state\n importance\n category\n version\n tasks(first: 100) {\n edges {\n node {\n id\n name\n description\n state\n timeEstimate\n version\n assignedTo {\n id\n fullName\n primaryEmailAddress\n }\n evidences(first: 50) {\n edges {\n node {\n id\n mimeType\n filename\n size\n state\n createdAt\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n }\n }\n}\n" } }; })(); -(node as any).hash = "e48b75fde2fcff42e868a0e68abd5809"; +(node as any).hash = "4710a9799b3daf286afdd430f026e6c2"; export default node; diff --git a/apps/console/src/pages/__generated__/ControlOverviewPageUpdateControlStateMutation.graphql.ts b/apps/console/src/pages/__generated__/ControlOverviewPageUpdateControlStateMutation.graphql.ts new file mode 100644 index 000000000..3fa3eea73 --- /dev/null +++ b/apps/console/src/pages/__generated__/ControlOverviewPageUpdateControlStateMutation.graphql.ts @@ -0,0 +1,129 @@ +/** + * @generated SignedSource<> + * @lightSyntaxTransform + * @nogrep + */ + +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck + +import { ConcreteRequest } from 'relay-runtime'; +export type ControlImportance = "ADVANCED" | "MANDATORY" | "PREFERRED"; +export type ControlState = "IMPLEMENTED" | "IN_PROGRESS" | "NOT_APPLICABLE" | "NOT_STARTED"; +export type UpdateControlInput = { + category?: string | null | undefined; + description?: string | null | undefined; + expectedVersion: number; + id: string; + importance?: ControlImportance | null | undefined; + name?: string | null | undefined; + state?: ControlState | null | undefined; +}; +export type ControlOverviewPageUpdateControlStateMutation$variables = { + input: UpdateControlInput; +}; +export type ControlOverviewPageUpdateControlStateMutation$data = { + readonly updateControl: { + readonly control: { + readonly id: string; + readonly state: ControlState; + readonly version: number; + }; + }; +}; +export type ControlOverviewPageUpdateControlStateMutation = { + response: ControlOverviewPageUpdateControlStateMutation$data; + variables: ControlOverviewPageUpdateControlStateMutation$variables; +}; + +const node: ConcreteRequest = (function(){ +var v0 = [ + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "input" + } +], +v1 = [ + { + "alias": null, + "args": [ + { + "kind": "Variable", + "name": "input", + "variableName": "input" + } + ], + "concreteType": "UpdateControlPayload", + "kind": "LinkedField", + "name": "updateControl", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "Control", + "kind": "LinkedField", + "name": "control", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "state", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "version", + "storageKey": null + } + ], + "storageKey": null + } + ], + "storageKey": null + } +]; +return { + "fragment": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Fragment", + "metadata": null, + "name": "ControlOverviewPageUpdateControlStateMutation", + "selections": (v1/*: any*/), + "type": "Mutation", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Operation", + "name": "ControlOverviewPageUpdateControlStateMutation", + "selections": (v1/*: any*/) + }, + "params": { + "cacheID": "f119c567d66788b2e3bed77950602ab5", + "id": null, + "metadata": {}, + "name": "ControlOverviewPageUpdateControlStateMutation", + "operationKind": "mutation", + "text": "mutation ControlOverviewPageUpdateControlStateMutation(\n $input: UpdateControlInput!\n) {\n updateControl(input: $input) {\n control {\n id\n state\n version\n }\n }\n}\n" + } +}; +})(); + +(node as any).hash = "a7792a068992b6e9b0c560426b8a99a4"; + +export default node;