Allow to edit control state without going to edit page
Signed-off-by: gearnode <bryan@frimin.fr>
This commit is contained in:
@@ -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<ControlOverviewPageUpdateControlStateMutationType>(
|
||||
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 (
|
||||
<>
|
||||
<Helmet>
|
||||
@@ -978,13 +1037,26 @@ function ControlOverviewPageContent({
|
||||
<Button variant="outline" size="sm" onClick={handleEditControl}>
|
||||
Edit Control
|
||||
</Button>
|
||||
<div
|
||||
className={`${getStateColor(
|
||||
data.control.state
|
||||
)} px-3 py-1 rounded-full text-sm`}
|
||||
<Select
|
||||
defaultValue={data.control.state}
|
||||
onValueChange={handleControlStateChange}
|
||||
>
|
||||
{formatState(data.control.state)}
|
||||
</div>
|
||||
<SelectTrigger className="w-[160px] h-8 text-sm">
|
||||
<div
|
||||
className={`${getStateColor(
|
||||
data.control.state
|
||||
)} px-2 py-0.5 rounded-full text-sm w-full text-center`}
|
||||
>
|
||||
{formatState(data.control.state)}
|
||||
</div>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="NOT_STARTED">Not Started</SelectItem>
|
||||
<SelectItem value="IN_PROGRESS">In Progress</SelectItem>
|
||||
<SelectItem value="IMPLEMENTED">Implemented</SelectItem>
|
||||
<SelectItem value="NOT_APPLICABLE">Not Applicable</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<div
|
||||
className={`${getImportanceColor(
|
||||
data.control.importance
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @generated SignedSource<<8f9fcd90d577e5e9f1b7ac31bbc1a14c>>
|
||||
* @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;
|
||||
|
||||
129
apps/console/src/pages/__generated__/ControlOverviewPageUpdateControlStateMutation.graphql.ts
generated
Normal file
129
apps/console/src/pages/__generated__/ControlOverviewPageUpdateControlStateMutation.graphql.ts
generated
Normal file
@@ -0,0 +1,129 @@
|
||||
/**
|
||||
* @generated SignedSource<<f8dd4ed041b15ddf484ff9b1735c69ed>>
|
||||
* @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;
|
||||
Reference in New Issue
Block a user