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,
|
PopoverContent,
|
||||||
PopoverTrigger,
|
PopoverTrigger,
|
||||||
} from "@/components/ui/popover";
|
} from "@/components/ui/popover";
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
} from "@/components/ui/select";
|
||||||
import ReactMarkdown from "react-markdown";
|
import ReactMarkdown from "react-markdown";
|
||||||
|
|
||||||
import { Helmet } from "react-helmet-async";
|
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 { ControlOverviewPageAssignTaskMutation as ControlOverviewPageAssignTaskMutationType } from "./__generated__/ControlOverviewPageAssignTaskMutation.graphql";
|
||||||
import type { ControlOverviewPageUnassignTaskMutation as ControlOverviewPageUnassignTaskMutationType } from "./__generated__/ControlOverviewPageUnassignTaskMutation.graphql";
|
import type { ControlOverviewPageUnassignTaskMutation as ControlOverviewPageUnassignTaskMutationType } from "./__generated__/ControlOverviewPageUnassignTaskMutation.graphql";
|
||||||
import type { ControlOverviewPageOrganizationQuery$data } from "./__generated__/ControlOverviewPageOrganizationQuery.graphql";
|
import type { ControlOverviewPageOrganizationQuery$data } from "./__generated__/ControlOverviewPageOrganizationQuery.graphql";
|
||||||
|
import type { ControlOverviewPageUpdateControlStateMutation as ControlOverviewPageUpdateControlStateMutationType } from "./__generated__/ControlOverviewPageUpdateControlStateMutation.graphql";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
|
|
||||||
// Function to format ISO8601 duration to human-readable format
|
// Function to format ISO8601 duration to human-readable format
|
||||||
@@ -115,6 +122,7 @@ const controlOverviewPageQuery = graphql`
|
|||||||
state
|
state
|
||||||
importance
|
importance
|
||||||
category
|
category
|
||||||
|
version
|
||||||
tasks(first: 100) @connection(key: "ControlOverviewPage_tasks") {
|
tasks(first: 100) @connection(key: "ControlOverviewPage_tasks") {
|
||||||
__id
|
__id
|
||||||
edges {
|
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`
|
const organizationQuery = graphql`
|
||||||
query ControlOverviewPageOrganizationQuery($organizationId: ID!) {
|
query ControlOverviewPageOrganizationQuery($organizationId: ID!) {
|
||||||
organization: node(id: $organizationId) {
|
organization: node(id: $organizationId) {
|
||||||
@@ -410,6 +432,11 @@ function ControlOverviewPageContent({
|
|||||||
unassignTaskMutation
|
unassignTaskMutation
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const [updateControlState] =
|
||||||
|
useMutation<ControlOverviewPageUpdateControlStateMutationType>(
|
||||||
|
updateControlStateMutation
|
||||||
|
);
|
||||||
|
|
||||||
const [isCreateTaskOpen, setIsCreateTaskOpen] = useState(false);
|
const [isCreateTaskOpen, setIsCreateTaskOpen] = useState(false);
|
||||||
const [newTaskName, setNewTaskName] = useState("");
|
const [newTaskName, setNewTaskName] = useState("");
|
||||||
const [newTaskDescription, setNewTaskDescription] = 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 (
|
return (
|
||||||
<>
|
<>
|
||||||
<Helmet>
|
<Helmet>
|
||||||
@@ -978,13 +1037,26 @@ function ControlOverviewPageContent({
|
|||||||
<Button variant="outline" size="sm" onClick={handleEditControl}>
|
<Button variant="outline" size="sm" onClick={handleEditControl}>
|
||||||
Edit Control
|
Edit Control
|
||||||
</Button>
|
</Button>
|
||||||
<div
|
<Select
|
||||||
className={`${getStateColor(
|
defaultValue={data.control.state}
|
||||||
data.control.state
|
onValueChange={handleControlStateChange}
|
||||||
)} px-3 py-1 rounded-full text-sm`}
|
|
||||||
>
|
>
|
||||||
{formatState(data.control.state)}
|
<SelectTrigger className="w-[160px] h-8 text-sm">
|
||||||
</div>
|
<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
|
<div
|
||||||
className={`${getImportanceColor(
|
className={`${getImportanceColor(
|
||||||
data.control.importance
|
data.control.importance
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* @generated SignedSource<<8f9fcd90d577e5e9f1b7ac31bbc1a14c>>
|
* @generated SignedSource<<84b34aa7e582728a8386d1fe1ef3fb58>>
|
||||||
* @lightSyntaxTransform
|
* @lightSyntaxTransform
|
||||||
* @nogrep
|
* @nogrep
|
||||||
*/
|
*/
|
||||||
@@ -55,6 +55,7 @@ export type ControlOverviewPageQuery$data = {
|
|||||||
};
|
};
|
||||||
}>;
|
}>;
|
||||||
};
|
};
|
||||||
|
readonly version?: number;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
export type ControlOverviewPageQuery = {
|
export type ControlOverviewPageQuery = {
|
||||||
@@ -123,14 +124,14 @@ v8 = {
|
|||||||
"alias": null,
|
"alias": null,
|
||||||
"args": null,
|
"args": null,
|
||||||
"kind": "ScalarField",
|
"kind": "ScalarField",
|
||||||
"name": "timeEstimate",
|
"name": "version",
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
},
|
},
|
||||||
v9 = {
|
v9 = {
|
||||||
"alias": null,
|
"alias": null,
|
||||||
"args": null,
|
"args": null,
|
||||||
"kind": "ScalarField",
|
"kind": "ScalarField",
|
||||||
"name": "version",
|
"name": "timeEstimate",
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
},
|
},
|
||||||
v10 = {
|
v10 = {
|
||||||
@@ -306,6 +307,7 @@ return {
|
|||||||
(v5/*: any*/),
|
(v5/*: any*/),
|
||||||
(v6/*: any*/),
|
(v6/*: any*/),
|
||||||
(v7/*: any*/),
|
(v7/*: any*/),
|
||||||
|
(v8/*: any*/),
|
||||||
{
|
{
|
||||||
"alias": "tasks",
|
"alias": "tasks",
|
||||||
"args": null,
|
"args": null,
|
||||||
@@ -334,8 +336,8 @@ return {
|
|||||||
(v3/*: any*/),
|
(v3/*: any*/),
|
||||||
(v4/*: any*/),
|
(v4/*: any*/),
|
||||||
(v5/*: any*/),
|
(v5/*: any*/),
|
||||||
(v8/*: any*/),
|
|
||||||
(v9/*: any*/),
|
(v9/*: any*/),
|
||||||
|
(v8/*: any*/),
|
||||||
(v10/*: any*/),
|
(v10/*: any*/),
|
||||||
{
|
{
|
||||||
"alias": "evidences",
|
"alias": "evidences",
|
||||||
@@ -395,6 +397,7 @@ return {
|
|||||||
(v5/*: any*/),
|
(v5/*: any*/),
|
||||||
(v6/*: any*/),
|
(v6/*: any*/),
|
||||||
(v7/*: any*/),
|
(v7/*: any*/),
|
||||||
|
(v8/*: any*/),
|
||||||
{
|
{
|
||||||
"alias": null,
|
"alias": null,
|
||||||
"args": (v16/*: any*/),
|
"args": (v16/*: any*/),
|
||||||
@@ -423,8 +426,8 @@ return {
|
|||||||
(v3/*: any*/),
|
(v3/*: any*/),
|
||||||
(v4/*: any*/),
|
(v4/*: any*/),
|
||||||
(v5/*: any*/),
|
(v5/*: any*/),
|
||||||
(v8/*: any*/),
|
|
||||||
(v9/*: any*/),
|
(v9/*: any*/),
|
||||||
|
(v8/*: any*/),
|
||||||
(v10/*: any*/),
|
(v10/*: any*/),
|
||||||
{
|
{
|
||||||
"alias": null,
|
"alias": null,
|
||||||
@@ -477,7 +480,7 @@ return {
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"params": {
|
"params": {
|
||||||
"cacheID": "1d26d59ea2232a213d01636e8d388919",
|
"cacheID": "4bcea497959829f998cdab71797c8675",
|
||||||
"id": null,
|
"id": null,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"connection": [
|
"connection": [
|
||||||
@@ -500,11 +503,11 @@ return {
|
|||||||
},
|
},
|
||||||
"name": "ControlOverviewPageQuery",
|
"name": "ControlOverviewPageQuery",
|
||||||
"operationKind": "query",
|
"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;
|
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