diff --git a/apps/console/src/pages/ControlOverviewPage.tsx b/apps/console/src/pages/ControlOverviewPage.tsx index c2bd6ab31..40ff80ab5 100644 --- a/apps/console/src/pages/ControlOverviewPage.tsx +++ b/apps/console/src/pages/ControlOverviewPage.tsx @@ -4,7 +4,7 @@ import { useState, useRef, DragEvent, - useMemo, + useCallback, } from "react"; import { useParams, useNavigate } from "react-router"; import { @@ -49,10 +49,7 @@ import type { ControlOverviewPageQuery as ControlOverviewPageQueryType } from ". import type { ControlOverviewPageUpdateTaskStateMutation as ControlOverviewPageUpdateTaskStateMutationType } from "./__generated__/ControlOverviewPageUpdateTaskStateMutation.graphql"; import type { ControlOverviewPageCreateTaskMutation as ControlOverviewPageCreateTaskMutationType } from "./__generated__/ControlOverviewPageCreateTaskMutation.graphql"; import type { ControlOverviewPageDeleteTaskMutation as ControlOverviewPageDeleteTaskMutationType } from "./__generated__/ControlOverviewPageDeleteTaskMutation.graphql"; -import type { - ControlOverviewPageUploadEvidenceMutation as ControlOverviewPageUploadEvidenceMutationType, - EvidenceState, -} from "./__generated__/ControlOverviewPageUploadEvidenceMutation.graphql"; +import type { ControlOverviewPageUploadEvidenceMutation as ControlOverviewPageUploadEvidenceMutationType } from "./__generated__/ControlOverviewPageUploadEvidenceMutation.graphql"; const controlOverviewPageQuery = graphql` query ControlOverviewPageQuery($controlId: ID!) { @@ -64,13 +61,16 @@ const controlOverviewPageQuery = graphql` state category tasks(first: 100) @connection(key: "ControlOverviewPage_tasks") { + __id edges { node { id name description state - evidences(first: 10) { + evidences(first: 50) + @connection(key: "ControlOverviewPage_evidences") { + __id edges { node { id @@ -152,29 +152,6 @@ const uploadEvidenceMutation = graphql` } `; -// Use the Relay-generated types with extensions for fields that might not be in the generated types yet -type EvidenceNode = { - id: string; - name: string; - fileUrl: string; - mimeType: string; - size: number; - state: EvidenceState; - createdAt: string; -}; - -type TaskNode = { - id: string; - name: string; - description: string; - state: string; - evidences?: { - edges: Array<{ - node: EvidenceNode | null; - } | null>; - }; -}; - function ControlOverviewPageContent({ queryRef, }: { @@ -199,9 +176,6 @@ function ControlOverviewPageContent({ useMutation( uploadEvidenceMutation ); - const control = data.control; - const tasks = - control?.tasks?.edges.map((edge) => edge?.node as TaskNode) ?? []; const [isCreateTaskOpen, setIsCreateTaskOpen] = useState(false); const [newTaskName, setNewTaskName] = useState(""); @@ -232,13 +206,19 @@ function ControlOverviewPageContent({ string | null >(null); - // Get the connection ID for Relay - const connectionId = useMemo(() => { - // The connection key is defined in the GraphQL query as "ControlOverviewPage_tasks" - return control?.id ? `client:${control.id}:tasks{"first":100}` : null; - }, [control?.id]); + const tasks = data.control.tasks?.edges.map((edge) => edge.node) || []; + + const getEvidenceConnectionId = useCallback( + (taskId: string) => { + const task = tasks.find((t) => t.id === taskId); + if (task?.evidences?.__id) { + return task.evidences.__id; + } + return null; + }, + [tasks] + ); - // Add global drag event handlers to detect when a file is being dragged useEffect(() => { const handleDragEnter = (e: globalThis.DragEvent) => { e.preventDefault(); @@ -289,11 +269,9 @@ function ControlOverviewPageContent({ }, optimisticResponse: { updateTaskState: { - taskEdge: { - node: { - id: taskId, - state: newState, - }, + task: { + id: taskId, + state: newState, }, }, }, @@ -325,7 +303,7 @@ function ControlOverviewPageContent({ return; } - if (!control?.id) { + if (!data.control.id) { toast({ title: "Error creating task", description: "Control ID is missing", @@ -336,9 +314,9 @@ function ControlOverviewPageContent({ createTask({ variables: { - connections: [], + connections: [`${data.control.tasks?.__id}`], input: { - controlId: control.id, + controlId: data.control.id, name: newTaskName, description: newTaskDescription, }, @@ -372,7 +350,7 @@ function ControlOverviewPageContent({ deleteTask({ variables: { - connections: connectionId ? [connectionId] : [], + connections: [`${data.control.tasks?.__id}`], input: { taskId: taskToDelete.id, }, @@ -413,6 +391,9 @@ function ControlOverviewPageContent({ const file = fileInputRef.current.files[0]; + // Get the evidence connection ID for this task + const evidenceConnectionId = getEvidenceConnectionId(taskForEvidence.id); + uploadEvidence({ variables: { input: { @@ -420,7 +401,7 @@ function ControlOverviewPageContent({ name: evidenceName || file.name, file: null, }, - connections: connectionId ? [connectionId] : [], + connections: evidenceConnectionId ? [evidenceConnectionId] : [], }, uploadables: { "input.file": file, @@ -479,6 +460,9 @@ function ControlOverviewPageContent({ description: `Uploading ${file.name}...`, }); + // Get the evidence connection ID for this task + const evidenceConnectionId = getEvidenceConnectionId(taskId); + uploadEvidence({ variables: { input: { @@ -486,7 +470,7 @@ function ControlOverviewPageContent({ name: file.name, file: null, }, - connections: connectionId ? [connectionId] : [], + connections: evidenceConnectionId ? [evidenceConnectionId] : [], }, uploadables: { "input.file": file, @@ -555,12 +539,12 @@ function ControlOverviewPageContent({ return ( <> - {control?.name || "Control"} - Probo + {data.control.name || "Control"} - Probo
-

{control?.name}

+

{data.control.name}

-

{control?.description}

+

{data.control.description}

@@ -587,14 +571,14 @@ function ControlOverviewPageContent({ >
- {control?.state === "IMPLEMENTED" + {data.control.state === "IMPLEMENTED" ? "Validated" : "Not validated"} @@ -818,60 +802,57 @@ function ControlOverviewPageContent({ {expandedEvidenceTaskId === task.id && (
- {task.evidences.edges.map( - (edge: { node: EvidenceNode | null } | null) => { - const evidence = edge?.node; - if (!evidence) return null; + {task.evidences.edges.map((edge) => { + if (!edge) return null; + const evidence = edge.node; + if (!evidence) return null; - return ( -
-
- {getFileIcon(evidence.mimeType)} -
-
- {evidence.name} -
-
- - {formatFileSize(evidence.size)} - - • - - {formatDate(evidence.createdAt)} - -
+ return ( +
+
+ {getFileIcon(evidence.mimeType)} +
+
+ {evidence.name} +
+
+ {formatFileSize(evidence.size)} + • + + {formatDate(evidence.createdAt)} +
-
- {evidence.mimeType.startsWith("image/") && ( - - - - )} +
+
+ {evidence.mimeType.startsWith("image/") && ( - + -
+ )} + + +
- ); - } - )} +
+ ); + })}
)} diff --git a/apps/console/src/pages/__generated__/ControlOverviewPageQuery.graphql.ts b/apps/console/src/pages/__generated__/ControlOverviewPageQuery.graphql.ts index 4723e1469..ce7eac119 100644 --- a/apps/console/src/pages/__generated__/ControlOverviewPageQuery.graphql.ts +++ b/apps/console/src/pages/__generated__/ControlOverviewPageQuery.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<> + * @generated SignedSource<<2ce0834e0bd8b3d2937b34ebf91f9a69>> * @lightSyntaxTransform * @nogrep */ @@ -23,10 +23,12 @@ export type ControlOverviewPageQuery$data = { readonly name?: string; readonly state?: ControlState; readonly tasks?: { + readonly __id: string; readonly edges: ReadonlyArray<{ readonly node: { readonly description: string; readonly evidences: { + readonly __id: string; readonly edges: ReadonlyArray<{ readonly node: { readonly createdAt: string; @@ -108,11 +110,55 @@ v7 = { "name": "__typename", "storageKey": null }, -v8 = [ +v8 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "cursor", + "storageKey": null +}, +v9 = { + "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 +}, +v10 = { + "kind": "ClientExtension", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__id", + "storageKey": null + } + ] +}, +v11 = [ { "alias": null, "args": null, - "concreteType": "TaskEdge", + "concreteType": "EvidenceEdge", "kind": "LinkedField", "name": "edges", "plural": true, @@ -120,130 +166,65 @@ v8 = [ { "alias": null, "args": null, - "concreteType": "Task", + "concreteType": "Evidence", "kind": "LinkedField", "name": "node", "plural": false, "selections": [ (v2/*: any*/), - (v3/*: any*/), - (v4/*: any*/), + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "fileUrl", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "mimeType", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "size", + "storageKey": null + }, (v5/*: any*/), { "alias": null, - "args": [ - { - "kind": "Literal", - "name": "first", - "value": 10 - } - ], - "concreteType": "EvidenceConnection", - "kind": "LinkedField", - "name": "evidences", - "plural": false, - "selections": [ - { - "alias": null, - "args": null, - "concreteType": "EvidenceEdge", - "kind": "LinkedField", - "name": "edges", - "plural": true, - "selections": [ - { - "alias": null, - "args": null, - "concreteType": "Evidence", - "kind": "LinkedField", - "name": "node", - "plural": false, - "selections": [ - (v2/*: any*/), - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "fileUrl", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "mimeType", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "size", - "storageKey": null - }, - (v5/*: any*/), - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "createdAt", - "storageKey": null - } - ], - "storageKey": null - } - ], - "storageKey": null - } - ], - "storageKey": "evidences(first:10)" + "args": null, + "kind": "ScalarField", + "name": "createdAt", + "storageKey": null }, (v7/*: any*/) ], "storageKey": null }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "cursor", - "storageKey": null - } + (v8/*: any*/) ], "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 - } + (v9/*: any*/), + (v10/*: any*/) ], -v9 = [ +v12 = [ { "kind": "Literal", "name": "first", "value": 100 } +], +v13 = [ + { + "kind": "Literal", + "name": "first", + "value": 50 + } ]; return { "fragment": { @@ -275,7 +256,48 @@ return { "kind": "LinkedField", "name": "__ControlOverviewPage_tasks_connection", "plural": false, - "selections": (v8/*: any*/), + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "TaskEdge", + "kind": "LinkedField", + "name": "edges", + "plural": true, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "Task", + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v2/*: any*/), + (v3/*: any*/), + (v4/*: any*/), + (v5/*: any*/), + { + "alias": "evidences", + "args": null, + "concreteType": "EvidenceConnection", + "kind": "LinkedField", + "name": "__ControlOverviewPage_evidences_connection", + "plural": false, + "selections": (v11/*: any*/), + "storageKey": null + }, + (v7/*: any*/) + ], + "storageKey": null + }, + (v8/*: any*/) + ], + "storageKey": null + }, + (v9/*: any*/), + (v10/*: any*/) + ], "storageKey": null } ], @@ -314,17 +336,67 @@ return { (v6/*: any*/), { "alias": null, - "args": (v9/*: any*/), + "args": (v12/*: any*/), "concreteType": "TaskConnection", "kind": "LinkedField", "name": "tasks", "plural": false, - "selections": (v8/*: any*/), + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "TaskEdge", + "kind": "LinkedField", + "name": "edges", + "plural": true, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "Task", + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v2/*: any*/), + (v3/*: any*/), + (v4/*: any*/), + (v5/*: any*/), + { + "alias": null, + "args": (v13/*: any*/), + "concreteType": "EvidenceConnection", + "kind": "LinkedField", + "name": "evidences", + "plural": false, + "selections": (v11/*: any*/), + "storageKey": "evidences(first:50)" + }, + { + "alias": null, + "args": (v13/*: any*/), + "filters": null, + "handle": "connection", + "key": "ControlOverviewPage_evidences", + "kind": "LinkedHandle", + "name": "evidences" + }, + (v7/*: any*/) + ], + "storageKey": null + }, + (v8/*: any*/) + ], + "storageKey": null + }, + (v9/*: any*/), + (v10/*: any*/) + ], "storageKey": "tasks(first:100)" }, { "alias": null, - "args": (v9/*: any*/), + "args": (v12/*: any*/), "filters": null, "handle": "connection", "key": "ControlOverviewPage_tasks", @@ -341,10 +413,16 @@ return { ] }, "params": { - "cacheID": "ae3ac6c53719fa3d9c1953ff40c75005", + "cacheID": "9738657b3d99bf6d85fd5333972e68f1", "id": null, "metadata": { "connection": [ + { + "count": null, + "cursor": null, + "direction": "forward", + "path": null + }, { "count": null, "cursor": null, @@ -358,11 +436,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 category\n tasks(first: 100) {\n edges {\n node {\n id\n name\n description\n state\n evidences(first: 10) {\n edges {\n node {\n id\n fileUrl\n mimeType\n size\n state\n createdAt\n }\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 category\n tasks(first: 100) {\n edges {\n node {\n id\n name\n description\n state\n evidences(first: 50) {\n edges {\n node {\n id\n fileUrl\n mimeType\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 = "ad8c4a4980229230ca295c4cb9a2c836"; +(node as any).hash = "5c2829a1e6bc18d96db9e608fbfc4d6f"; export default node;