diff --git a/apps/console/src/components/skeletons/ControlSkeleton.tsx b/apps/console/src/components/skeletons/ControlSkeleton.tsx new file mode 100644 index 000000000..9da502f31 --- /dev/null +++ b/apps/console/src/components/skeletons/ControlSkeleton.tsx @@ -0,0 +1,26 @@ +import { ActionDropdown, Skeleton } from "@probo/ui"; +import { Button, IconPencil } from "@probo/ui"; +import { useTranslate } from "@probo/i18n"; + +/** + * Skeleton state for the framework control panel + */ +export function ControlSkeleton() { + const { __ } = useTranslate(); + return ( +
+
+ +
+ + +
+
+ + + +
+ ); +} diff --git a/apps/console/src/components/skeletons/LinkCardSkeleton.tsx b/apps/console/src/components/skeletons/LinkCardSkeleton.tsx new file mode 100644 index 000000000..534d56963 --- /dev/null +++ b/apps/console/src/components/skeletons/LinkCardSkeleton.tsx @@ -0,0 +1,5 @@ +import { Skeleton } from "@probo/ui"; + +export function LinkCardSkeleton() { + return ; +} diff --git a/apps/console/src/hooks/graph/FrameworkGraph.ts b/apps/console/src/hooks/graph/FrameworkGraph.ts index e525b30bf..9db2e0b7a 100644 --- a/apps/console/src/hooks/graph/FrameworkGraph.ts +++ b/apps/console/src/hooks/graph/FrameworkGraph.ts @@ -88,3 +88,37 @@ export const frameworkNodeQuery = graphql` } } `; + +export const frameworkControlNodeQuery = graphql` + query FrameworkGraphControlNodeQuery($controlId: ID!) { + node(id: $controlId) { + ... on Control { + id + name + sectionTitle + description + ...FrameworkControlDialogFragment + measures(first: 100) + @connection(key: "FrameworkGraphControl_measures") { + __id + edges { + node { + id + ...LinkedMeasuresCardFragment + } + } + } + documents(first: 100) + @connection(key: "FrameworkGraphControl_documents") { + __id + edges { + node { + id + ...LinkedDocumentsCardFragment + } + } + } + } + } + } +`; diff --git a/apps/console/src/hooks/graph/__generated__/FrameworkGraphControlNodeQuery.graphql.ts b/apps/console/src/hooks/graph/__generated__/FrameworkGraphControlNodeQuery.graphql.ts new file mode 100644 index 000000000..6f61473a9 --- /dev/null +++ b/apps/console/src/hooks/graph/__generated__/FrameworkGraphControlNodeQuery.graphql.ts @@ -0,0 +1,508 @@ +/** + * @generated SignedSource<<9972eafa19f49d7beea0458c5b9c06d9>> + * @lightSyntaxTransform + * @nogrep + */ + +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck + +import { ConcreteRequest } from 'relay-runtime'; +import { FragmentRefs } from "relay-runtime"; +export type FrameworkGraphControlNodeQuery$variables = { + controlId: string; +}; +export type FrameworkGraphControlNodeQuery$data = { + readonly node: { + readonly description?: string; + readonly documents?: { + readonly __id: string; + readonly edges: ReadonlyArray<{ + readonly node: { + readonly id: string; + readonly " $fragmentSpreads": FragmentRefs<"LinkedDocumentsCardFragment">; + }; + }>; + }; + readonly id?: string; + readonly measures?: { + readonly __id: string; + readonly edges: ReadonlyArray<{ + readonly node: { + readonly id: string; + readonly " $fragmentSpreads": FragmentRefs<"LinkedMeasuresCardFragment">; + }; + }>; + }; + readonly name?: string; + readonly sectionTitle?: string; + readonly " $fragmentSpreads": FragmentRefs<"FrameworkControlDialogFragment">; + }; +}; +export type FrameworkGraphControlNodeQuery = { + response: FrameworkGraphControlNodeQuery$data; + variables: FrameworkGraphControlNodeQuery$variables; +}; + +const node: ConcreteRequest = (function(){ +var v0 = [ + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "controlId" + } +], +v1 = [ + { + "kind": "Variable", + "name": "id", + "variableName": "controlId" + } +], +v2 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null +}, +v3 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null +}, +v4 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "sectionTitle", + "storageKey": null +}, +v5 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "description", + "storageKey": null +}, +v6 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null +}, +v7 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "cursor", + "storageKey": null +}, +v8 = { + "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 = { + "kind": "ClientExtension", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__id", + "storageKey": null + } + ] +}, +v10 = [ + { + "kind": "Literal", + "name": "first", + "value": 100 + } +]; +return { + "fragment": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Fragment", + "metadata": null, + "name": "FrameworkGraphControlNodeQuery", + "selections": [ + { + "alias": null, + "args": (v1/*: any*/), + "concreteType": null, + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + { + "kind": "InlineFragment", + "selections": [ + (v2/*: any*/), + (v3/*: any*/), + (v4/*: any*/), + (v5/*: any*/), + { + "args": null, + "kind": "FragmentSpread", + "name": "FrameworkControlDialogFragment" + }, + { + "alias": "measures", + "args": null, + "concreteType": "MeasureConnection", + "kind": "LinkedField", + "name": "__FrameworkGraphControl_measures_connection", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "MeasureEdge", + "kind": "LinkedField", + "name": "edges", + "plural": true, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "Measure", + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v2/*: any*/), + { + "args": null, + "kind": "FragmentSpread", + "name": "LinkedMeasuresCardFragment" + }, + (v6/*: any*/) + ], + "storageKey": null + }, + (v7/*: any*/) + ], + "storageKey": null + }, + (v8/*: any*/), + (v9/*: any*/) + ], + "storageKey": null + }, + { + "alias": "documents", + "args": null, + "concreteType": "DocumentConnection", + "kind": "LinkedField", + "name": "__FrameworkGraphControl_documents_connection", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "DocumentEdge", + "kind": "LinkedField", + "name": "edges", + "plural": true, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "Document", + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v2/*: any*/), + { + "args": null, + "kind": "FragmentSpread", + "name": "LinkedDocumentsCardFragment" + }, + (v6/*: any*/) + ], + "storageKey": null + }, + (v7/*: any*/) + ], + "storageKey": null + }, + (v8/*: any*/), + (v9/*: any*/) + ], + "storageKey": null + } + ], + "type": "Control", + "abstractKey": null + } + ], + "storageKey": null + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Operation", + "name": "FrameworkGraphControlNodeQuery", + "selections": [ + { + "alias": null, + "args": (v1/*: any*/), + "concreteType": null, + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v6/*: any*/), + (v2/*: any*/), + { + "kind": "InlineFragment", + "selections": [ + (v3/*: any*/), + (v4/*: any*/), + (v5/*: any*/), + { + "alias": null, + "args": (v10/*: any*/), + "concreteType": "MeasureConnection", + "kind": "LinkedField", + "name": "measures", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "MeasureEdge", + "kind": "LinkedField", + "name": "edges", + "plural": true, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "Measure", + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v2/*: any*/), + (v3/*: any*/), + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "state", + "storageKey": null + }, + (v6/*: any*/) + ], + "storageKey": null + }, + (v7/*: any*/) + ], + "storageKey": null + }, + (v8/*: any*/), + (v9/*: any*/) + ], + "storageKey": "measures(first:100)" + }, + { + "alias": null, + "args": (v10/*: any*/), + "filters": null, + "handle": "connection", + "key": "FrameworkGraphControl_measures", + "kind": "LinkedHandle", + "name": "measures" + }, + { + "alias": null, + "args": (v10/*: any*/), + "concreteType": "DocumentConnection", + "kind": "LinkedField", + "name": "documents", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "DocumentEdge", + "kind": "LinkedField", + "name": "edges", + "plural": true, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "Document", + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v2/*: any*/), + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "title", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "createdAt", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "documentType", + "storageKey": null + }, + { + "alias": null, + "args": [ + { + "kind": "Literal", + "name": "first", + "value": 1 + } + ], + "concreteType": "DocumentVersionConnection", + "kind": "LinkedField", + "name": "versions", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "DocumentVersionEdge", + "kind": "LinkedField", + "name": "edges", + "plural": true, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "DocumentVersion", + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v2/*: any*/), + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "status", + "storageKey": null + } + ], + "storageKey": null + } + ], + "storageKey": null + } + ], + "storageKey": "versions(first:1)" + }, + (v6/*: any*/) + ], + "storageKey": null + }, + (v7/*: any*/) + ], + "storageKey": null + }, + (v8/*: any*/), + (v9/*: any*/) + ], + "storageKey": "documents(first:100)" + }, + { + "alias": null, + "args": (v10/*: any*/), + "filters": null, + "handle": "connection", + "key": "FrameworkGraphControl_documents", + "kind": "LinkedHandle", + "name": "documents" + } + ], + "type": "Control", + "abstractKey": null + } + ], + "storageKey": null + } + ] + }, + "params": { + "cacheID": "165ee5f4a3579e54fb7acafd6fd7ab12", + "id": null, + "metadata": { + "connection": [ + { + "count": null, + "cursor": null, + "direction": "forward", + "path": [ + "node", + "measures" + ] + }, + { + "count": null, + "cursor": null, + "direction": "forward", + "path": [ + "node", + "documents" + ] + } + ] + }, + "name": "FrameworkGraphControlNodeQuery", + "operationKind": "query", + "text": "query FrameworkGraphControlNodeQuery(\n $controlId: ID!\n) {\n node(id: $controlId) {\n __typename\n ... on Control {\n id\n name\n sectionTitle\n description\n ...FrameworkControlDialogFragment\n measures(first: 100) {\n edges {\n node {\n id\n ...LinkedMeasuresCardFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n documents(first: 100) {\n edges {\n node {\n id\n ...LinkedDocumentsCardFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n }\n id\n }\n}\n\nfragment FrameworkControlDialogFragment on Control {\n id\n name\n description\n sectionTitle\n}\n\nfragment LinkedDocumentsCardFragment on Document {\n id\n title\n createdAt\n documentType\n versions(first: 1) {\n edges {\n node {\n id\n status\n }\n }\n }\n}\n\nfragment LinkedMeasuresCardFragment on Measure {\n id\n name\n state\n}\n" + } +}; +})(); + +(node as any).hash = "a2b0b5449af380ea2843aab6ce187b75"; + +export default node; diff --git a/apps/console/src/hooks/graph/__generated__/FrameworkGraphNodeQuery.graphql.ts b/apps/console/src/hooks/graph/__generated__/FrameworkGraphNodeQuery.graphql.ts index 270b85819..fe82746f4 100644 --- a/apps/console/src/hooks/graph/__generated__/FrameworkGraphNodeQuery.graphql.ts +++ b/apps/console/src/hooks/graph/__generated__/FrameworkGraphNodeQuery.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<5cb97bc6e333575149d6cc4418370d45>> + * @generated SignedSource<<5627e402cef1ec879c133d54980b26ab>> * @lightSyntaxTransform * @nogrep */ @@ -53,71 +53,6 @@ v3 = { "kind": "ScalarField", "name": "name", "storageKey": null -}, -v4 = { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "__typename", - "storageKey": null -}, -v5 = { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "description", - "storageKey": null -}, -v6 = [ - { - "kind": "Literal", - "name": "first", - "value": 100 - } -], -v7 = { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "cursor", - "storageKey": null -}, -v8 = { - "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 = { - "kind": "ClientExtension", - "selections": [ - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "__id", - "storageKey": null - } - ] }; return { "fragment": { @@ -169,16 +104,34 @@ return { "name": "node", "plural": false, "selections": [ - (v4/*: any*/), + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null + }, (v2/*: any*/), { "kind": "InlineFragment", "selections": [ (v3/*: any*/), - (v5/*: any*/), { "alias": null, - "args": (v6/*: any*/), + "args": null, + "kind": "ScalarField", + "name": "description", + "storageKey": null + }, + { + "alias": null, + "args": [ + { + "kind": "Literal", + "name": "first", + "value": 100 + } + ], "concreteType": "ControlConnection", "kind": "LinkedField", "name": "controls", @@ -208,185 +161,25 @@ return { "name": "sectionTitle", "storageKey": null }, - (v3/*: any*/), - (v5/*: any*/), - { - "alias": null, - "args": (v6/*: any*/), - "concreteType": "MeasureConnection", - "kind": "LinkedField", - "name": "measures", - "plural": false, - "selections": [ - { - "alias": null, - "args": null, - "concreteType": "MeasureEdge", - "kind": "LinkedField", - "name": "edges", - "plural": true, - "selections": [ - { - "alias": null, - "args": null, - "concreteType": "Measure", - "kind": "LinkedField", - "name": "node", - "plural": false, - "selections": [ - (v2/*: any*/), - (v3/*: any*/), - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "state", - "storageKey": null - }, - (v4/*: any*/) - ], - "storageKey": null - }, - (v7/*: any*/) - ], - "storageKey": null - }, - (v8/*: any*/), - (v9/*: any*/) - ], - "storageKey": "measures(first:100)" - }, - { - "alias": null, - "args": (v6/*: any*/), - "filters": null, - "handle": "connection", - "key": "FrameworkDetailPage_measures", - "kind": "LinkedHandle", - "name": "measures" - }, - { - "alias": null, - "args": (v6/*: any*/), - "concreteType": "DocumentConnection", - "kind": "LinkedField", - "name": "documents", - "plural": false, - "selections": [ - { - "alias": null, - "args": null, - "concreteType": "DocumentEdge", - "kind": "LinkedField", - "name": "edges", - "plural": true, - "selections": [ - { - "alias": null, - "args": null, - "concreteType": "Document", - "kind": "LinkedField", - "name": "node", - "plural": false, - "selections": [ - (v2/*: any*/), - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "title", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "createdAt", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "documentType", - "storageKey": null - }, - { - "alias": null, - "args": [ - { - "kind": "Literal", - "name": "first", - "value": 1 - } - ], - "concreteType": "DocumentVersionConnection", - "kind": "LinkedField", - "name": "versions", - "plural": false, - "selections": [ - { - "alias": null, - "args": null, - "concreteType": "DocumentVersionEdge", - "kind": "LinkedField", - "name": "edges", - "plural": true, - "selections": [ - { - "alias": null, - "args": null, - "concreteType": "DocumentVersion", - "kind": "LinkedField", - "name": "node", - "plural": false, - "selections": [ - (v2/*: any*/), - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "status", - "storageKey": null - } - ], - "storageKey": null - } - ], - "storageKey": null - } - ], - "storageKey": "versions(first:1)" - }, - (v4/*: any*/) - ], - "storageKey": null - }, - (v7/*: any*/) - ], - "storageKey": null - }, - (v8/*: any*/), - (v9/*: any*/) - ], - "storageKey": "documents(first:100)" - }, - { - "alias": null, - "args": (v6/*: any*/), - "filters": null, - "handle": "connection", - "key": "FrameworkDetailPage_documents", - "kind": "LinkedHandle", - "name": "documents" - } + (v3/*: any*/) ], "storageKey": null } ], "storageKey": null }, - (v9/*: any*/) + { + "kind": "ClientExtension", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__id", + "storageKey": null + } + ] + } ], "storageKey": "controls(first:100)" } @@ -400,12 +193,12 @@ return { ] }, "params": { - "cacheID": "4b21f0ec7a74eed125d96e4cfc7f94fd", + "cacheID": "28ea34e7b455cffd5283428ef567963a", "id": null, "metadata": {}, "name": "FrameworkGraphNodeQuery", "operationKind": "query", - "text": "query FrameworkGraphNodeQuery(\n $frameworkId: ID!\n) {\n node(id: $frameworkId) {\n __typename\n ... on Framework {\n id\n name\n ...FrameworkDetailPageFragment\n }\n id\n }\n}\n\nfragment FrameworkControlDialogFragment on Control {\n id\n name\n description\n sectionTitle\n}\n\nfragment FrameworkDetailPageFragment on Framework {\n id\n name\n description\n controls(first: 100) {\n edges {\n node {\n id\n sectionTitle\n name\n description\n ...FrameworkControlDialogFragment\n measures(first: 100) {\n edges {\n node {\n id\n ...LinkedMeasuresCardFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n documents(first: 100) {\n edges {\n node {\n id\n ...LinkedDocumentsCardFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n }\n }\n }\n}\n\nfragment LinkedDocumentsCardFragment on Document {\n id\n title\n createdAt\n documentType\n versions(first: 1) {\n edges {\n node {\n id\n status\n }\n }\n }\n}\n\nfragment LinkedMeasuresCardFragment on Measure {\n id\n name\n state\n}\n" + "text": "query FrameworkGraphNodeQuery(\n $frameworkId: ID!\n) {\n node(id: $frameworkId) {\n __typename\n ... on Framework {\n id\n name\n ...FrameworkDetailPageFragment\n }\n id\n }\n}\n\nfragment FrameworkDetailPageFragment on Framework {\n id\n name\n description\n controls(first: 100) {\n edges {\n node {\n id\n sectionTitle\n name\n }\n }\n }\n}\n" } }; })(); diff --git a/apps/console/src/pages/organizations/frameworks/FrameworkControlPage.tsx b/apps/console/src/pages/organizations/frameworks/FrameworkControlPage.tsx new file mode 100644 index 000000000..87fe169f8 --- /dev/null +++ b/apps/console/src/pages/organizations/frameworks/FrameworkControlPage.tsx @@ -0,0 +1,195 @@ +import { + useMutation, + usePreloadedQuery, + type PreloadedQuery, +} from "react-relay"; +import { graphql } from "relay-runtime"; +import { + ActionDropdown, + Button, + DropdownItem, + IconPencil, + IconTrashCan, + useConfirm, +} from "@probo/ui"; +import { useTranslate } from "@probo/i18n"; +import { LinkedMeasuresCard } from "/components/measures/LinkedMeasuresCard"; +import { useNavigate, useOutletContext } from "react-router"; +import { useOrganizationId } from "/hooks/useOrganizationId"; +import { LinkedDocumentsCard } from "/components/documents/LinkedDocumentsCard"; +import { FrameworkControlDialog } from "./dialogs/FrameworkControlDialog"; +import { promisifyMutation } from "@probo/helpers"; +import type { FrameworkGraphControlNodeQuery } from "/hooks/graph/__generated__/FrameworkGraphControlNodeQuery.graphql"; +import { frameworkControlNodeQuery } from "/hooks/graph/FrameworkGraph"; +import type { FrameworkDetailPageFragment$data } from "./__generated__/FrameworkDetailPageFragment.graphql"; + +const attachMeasureMutation = graphql` + mutation FrameworkControlPageAttachMutation( + $input: CreateControlMeasureMappingInput! + $connections: [ID!]! + ) { + createControlMeasureMapping(input: $input) { + measureEdge @prependEdge(connections: $connections) { + node { + id + ...LinkedMeasuresCardFragment + } + } + } + } +`; + +const detachMeasureMutation = graphql` + mutation FrameworkControlPageDetachMutation( + $input: DeleteControlMeasureMappingInput! + $connections: [ID!]! + ) { + deleteControlMeasureMapping(input: $input) { + deletedMeasureId @deleteEdge(connections: $connections) + } + } +`; + +const attachDocumentMutation = graphql` + mutation FrameworkControlPageAttachDocumentMutation( + $input: CreateControlDocumentMappingInput! + $connections: [ID!]! + ) { + createControlDocumentMapping(input: $input) { + documentEdge @prependEdge(connections: $connections) { + node { + id + ...LinkedDocumentsCardFragment + } + } + } + } +`; + +const detachDocumentMutation = graphql` + mutation FrameworkControlPageDetachDocumentMutation( + $input: DeleteControlDocumentMappingInput! + $connections: [ID!]! + ) { + deleteControlDocumentMapping(input: $input) { + deletedDocumentId @deleteEdge(connections: $connections) + } + } +`; + +const deleteControlMutation = graphql` + mutation FrameworkControlPageDeleteControlMutation( + $input: DeleteControlInput! + $connections: [ID!]! + ) { + deleteControl(input: $input) { + deletedControlId @deleteEdge(connections: $connections) + } + } +`; + +type Props = { + queryRef: PreloadedQuery; +}; + +/** + * Display the control detail on the right panel + */ +export default function FrameworkControlPage({ queryRef }: Props) { + const { __ } = useTranslate(); + const { framework } = useOutletContext<{ + framework: FrameworkDetailPageFragment$data; + }>(); + const connectionId = framework.controls.__id; + const control = usePreloadedQuery(frameworkControlNodeQuery, queryRef).node; + const organizationId = useOrganizationId(); + const confirm = useConfirm(); + const navigate = useNavigate(); + // Mutations + const [detachMeasure, isDetachingMeasure] = useMutation( + detachMeasureMutation + ); + const [attachMeasure, isAttachingMeasure] = useMutation( + attachMeasureMutation + ); + const [detachDocument, isDetachingDocument] = useMutation( + detachDocumentMutation + ); + const [attachDocument, isAttachingDocument] = useMutation( + attachDocumentMutation + ); + const [deleteControl] = useMutation(deleteControlMutation); + + const onDelete = () => { + confirm( + () => { + return promisifyMutation(deleteControl)({ + variables: { + input: { + controlId: control.id, + }, + connections: [connectionId], + }, + onCompleted: () => { + navigate( + `/organizations/${organizationId}/frameworks/${framework.id}` + ); + }, + }); + }, + { + message: __("Are you sure you want to delete this control?"), + } + ); + }; + + return ( +
+
+
+ {control.sectionTitle} +
+
+ + + + + + {__("Delete")} + + +
+
+ +
{control.name}
+ edge.node) ?? []} + params={{ controlId: control.id }} + connectionId={control.measures?.__id!} + onAttach={attachMeasure} + onDetach={detachMeasure} + disabled={isAttachingMeasure || isDetachingMeasure} + /> + edge.node) ?? []} + params={{ controlId: control.id }} + connectionId={control.documents?.__id!} + onAttach={attachDocument} + onDetach={detachDocument} + disabled={isAttachingDocument || isDetachingDocument} + /> +
+ ); +} diff --git a/apps/console/src/pages/organizations/frameworks/FrameworkDetailPage.tsx b/apps/console/src/pages/organizations/frameworks/FrameworkDetailPage.tsx index 67a4511bf..582bb17ed 100644 --- a/apps/console/src/pages/organizations/frameworks/FrameworkDetailPage.tsx +++ b/apps/console/src/pages/organizations/frameworks/FrameworkDetailPage.tsx @@ -1,6 +1,5 @@ import { useFragment, - useMutation, usePreloadedQuery, type PreloadedQuery, } from "react-relay"; @@ -15,7 +14,6 @@ import { IconPlusLarge, IconTrashCan, PageHeader, - useConfirm, } from "@probo/ui"; import { FrameworkLogo } from "/components/FrameworkLogo"; import { @@ -24,20 +22,12 @@ import { useDeleteFrameworkMutation, } from "/hooks/graph/FrameworkGraph"; import { useTranslate } from "@probo/i18n"; -import { LinkedMeasuresCard } from "/components/measures/LinkedMeasuresCard"; -import { useNavigate, useParams } from "react-router"; +import { Navigate, Outlet, useNavigate, useParams } from "react-router"; import { useOrganizationId } from "/hooks/useOrganizationId"; import type { FrameworkGraphNodeQuery } from "/hooks/graph/__generated__/FrameworkGraphNodeQuery.graphql"; -import type { - FrameworkDetailPageFragment$data, - FrameworkDetailPageFragment$key, -} from "./__generated__/FrameworkDetailPageFragment.graphql"; -import { LinkedDocumentsCard } from "/components/documents/LinkedDocumentsCard"; +import type { FrameworkDetailPageFragment$key } from "./__generated__/FrameworkDetailPageFragment.graphql"; import { FrameworkFormDialog } from "./dialogs/FrameworkFormDialog"; import { FrameworkControlDialog } from "./dialogs/FrameworkControlDialog"; -import type { FrameworkControlDialogFragment$key } from "./dialogs/__generated__/FrameworkControlDialogFragment.graphql"; -import type { NodeOf } from "/types"; -import { promisifyMutation } from "@probo/helpers"; const frameworkDetailFragment = graphql` fragment FrameworkDetailPageFragment on Framework { @@ -51,88 +41,12 @@ const frameworkDetailFragment = graphql` id sectionTitle name - description - ...FrameworkControlDialogFragment - measures(first: 100) - @connection(key: "FrameworkDetailPage_measures") { - __id - edges { - node { - id - ...LinkedMeasuresCardFragment - } - } - } - documents(first: 100) - @connection(key: "FrameworkDetailPage_documents") { - __id - edges { - node { - id - ...LinkedDocumentsCardFragment - } - } - } } } } } `; -const attachMeasureMutation = graphql` - mutation FrameworkDetailPageAttachMutation( - $input: CreateControlMeasureMappingInput! - $connections: [ID!]! - ) { - createControlMeasureMapping(input: $input) { - measureEdge @prependEdge(connections: $connections) { - node { - id - ...LinkedMeasuresCardFragment - } - } - } - } -`; - -const detachMeasureMutation = graphql` - mutation FrameworkDetailPageDetachMutation( - $input: DeleteControlMeasureMappingInput! - $connections: [ID!]! - ) { - deleteControlMeasureMapping(input: $input) { - deletedMeasureId @deleteEdge(connections: $connections) - } - } -`; - -const attachDocumentMutation = graphql` - mutation FrameworkDetailPageAttachDocumentMutation( - $input: CreateControlDocumentMappingInput! - $connections: [ID!]! - ) { - createControlDocumentMapping(input: $input) { - documentEdge @prependEdge(connections: $connections) { - node { - id - ...LinkedDocumentsCardFragment - } - } - } - } -`; - -const detachDocumentMutation = graphql` - mutation FrameworkDetailPageDetachDocumentMutation( - $input: DeleteControlDocumentMappingInput! - $connections: [ID!]! - ) { - deleteControlDocumentMapping(input: $input) { - deletedDocumentId @deleteEdge(connections: $connections) - } - } -`; - type Props = { queryRef: PreloadedQuery; }; @@ -166,6 +80,14 @@ export default function FrameworkDetailPage(props: Props) { }); }; + if (!controlId) { + return ( + + ); + } + return (
@@ -217,137 +139,8 @@ export default function FrameworkDetailPage(props: Props) {
- {selectedControl ? ( - - ) : ( -
-
- {__("No control selected")} -
-
- )} + ); } - -const deleteControlMutation = graphql` - mutation FrameworkDetailPageDeleteControlMutation( - $input: DeleteControlInput! - $connections: [ID!]! - ) { - deleteControl(input: $input) { - deletedControlId @deleteEdge(connections: $connections) - } - } -`; - -/** - * Right side pannel (showing the content of the selected control) - */ -function ControlContent({ - control, - frameworkId, - connectionId, -}: { - control: NodeOf & - FrameworkControlDialogFragment$key; - frameworkId: string; - connectionId: string; -}) { - const { __ } = useTranslate(); - const organizationId = useOrganizationId(); - const confirm = useConfirm(); - const navigate = useNavigate(); - // Mutations - const [detachMeasure, isDetachingMeasure] = useMutation( - detachMeasureMutation - ); - const [attachMeasure, isAttachingMeasure] = useMutation( - attachMeasureMutation - ); - const [detachDocument, isDetachingDocument] = useMutation( - detachDocumentMutation - ); - const [attachDocument, isAttachingDocument] = useMutation( - attachDocumentMutation - ); - const [deleteControl] = useMutation(deleteControlMutation); - - const onDelete = () => { - confirm( - () => { - return promisifyMutation(deleteControl)({ - variables: { - input: { - controlId: control.id, - }, - connections: [connectionId], - }, - onCompleted: () => { - navigate( - `/organizations/${organizationId}/frameworks/${frameworkId}` - ); - }, - }); - }, - { - message: __("Are you sure you want to delete this control?"), - } - ); - }; - - return ( -
-
-
- {control.sectionTitle} -
-
- - - - - - {__("Delete")} - - -
-
- -
{control.name}
- edge.node) ?? []} - params={{ controlId: control.id }} - connectionId={control.measures.__id!} - onAttach={attachMeasure} - onDetach={detachMeasure} - disabled={isAttachingMeasure || isDetachingMeasure} - /> - edge.node) ?? []} - params={{ controlId: control.id }} - connectionId={control.documents.__id!} - onAttach={attachDocument} - onDetach={detachDocument} - disabled={isAttachingDocument || isDetachingDocument} - /> -
- ); -} diff --git a/apps/console/src/pages/organizations/frameworks/__generated__/FrameworkDetailPageAttachDocumentMutation.graphql.ts b/apps/console/src/pages/organizations/frameworks/__generated__/FrameworkControlPageAttachDocumentMutation.graphql.ts similarity index 85% rename from apps/console/src/pages/organizations/frameworks/__generated__/FrameworkDetailPageAttachDocumentMutation.graphql.ts rename to apps/console/src/pages/organizations/frameworks/__generated__/FrameworkControlPageAttachDocumentMutation.graphql.ts index 6f99a2bd2..8fad6cfbe 100644 --- a/apps/console/src/pages/organizations/frameworks/__generated__/FrameworkDetailPageAttachDocumentMutation.graphql.ts +++ b/apps/console/src/pages/organizations/frameworks/__generated__/FrameworkControlPageAttachDocumentMutation.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<7d4a807c299030c97164951cbe8ef067>> + * @generated SignedSource<<47d493f6b225797004c11ba908c1e7d2>> * @lightSyntaxTransform * @nogrep */ @@ -14,11 +14,11 @@ export type CreateControlDocumentMappingInput = { controlId: string; documentId: string; }; -export type FrameworkDetailPageAttachDocumentMutation$variables = { +export type FrameworkControlPageAttachDocumentMutation$variables = { connections: ReadonlyArray; input: CreateControlDocumentMappingInput; }; -export type FrameworkDetailPageAttachDocumentMutation$data = { +export type FrameworkControlPageAttachDocumentMutation$data = { readonly createControlDocumentMapping: { readonly documentEdge: { readonly node: { @@ -28,9 +28,9 @@ export type FrameworkDetailPageAttachDocumentMutation$data = { }; }; }; -export type FrameworkDetailPageAttachDocumentMutation = { - response: FrameworkDetailPageAttachDocumentMutation$data; - variables: FrameworkDetailPageAttachDocumentMutation$variables; +export type FrameworkControlPageAttachDocumentMutation = { + response: FrameworkControlPageAttachDocumentMutation$data; + variables: FrameworkControlPageAttachDocumentMutation$variables; }; const node: ConcreteRequest = (function(){ @@ -66,7 +66,7 @@ return { ], "kind": "Fragment", "metadata": null, - "name": "FrameworkDetailPageAttachDocumentMutation", + "name": "FrameworkControlPageAttachDocumentMutation", "selections": [ { "alias": null, @@ -118,7 +118,7 @@ return { (v0/*: any*/) ], "kind": "Operation", - "name": "FrameworkDetailPageAttachDocumentMutation", + "name": "FrameworkControlPageAttachDocumentMutation", "selections": [ { "alias": null, @@ -241,16 +241,16 @@ return { ] }, "params": { - "cacheID": "968bd0beeabab861ca610804640e5e11", + "cacheID": "857c472a56bba83b8249bb6f3b97eea2", "id": null, "metadata": {}, - "name": "FrameworkDetailPageAttachDocumentMutation", + "name": "FrameworkControlPageAttachDocumentMutation", "operationKind": "mutation", - "text": "mutation FrameworkDetailPageAttachDocumentMutation(\n $input: CreateControlDocumentMappingInput!\n) {\n createControlDocumentMapping(input: $input) {\n documentEdge {\n node {\n id\n ...LinkedDocumentsCardFragment\n }\n }\n }\n}\n\nfragment LinkedDocumentsCardFragment on Document {\n id\n title\n createdAt\n documentType\n versions(first: 1) {\n edges {\n node {\n id\n status\n }\n }\n }\n}\n" + "text": "mutation FrameworkControlPageAttachDocumentMutation(\n $input: CreateControlDocumentMappingInput!\n) {\n createControlDocumentMapping(input: $input) {\n documentEdge {\n node {\n id\n ...LinkedDocumentsCardFragment\n }\n }\n }\n}\n\nfragment LinkedDocumentsCardFragment on Document {\n id\n title\n createdAt\n documentType\n versions(first: 1) {\n edges {\n node {\n id\n status\n }\n }\n }\n}\n" } }; })(); -(node as any).hash = "26c53db439feba2b2d65c3e3a9a973c7"; +(node as any).hash = "6f019fa6bdfccc4cddd80f0de770440f"; export default node; diff --git a/apps/console/src/pages/organizations/frameworks/__generated__/FrameworkDetailPageAttachMutation.graphql.ts b/apps/console/src/pages/organizations/frameworks/__generated__/FrameworkControlPageAttachMutation.graphql.ts similarity index 82% rename from apps/console/src/pages/organizations/frameworks/__generated__/FrameworkDetailPageAttachMutation.graphql.ts rename to apps/console/src/pages/organizations/frameworks/__generated__/FrameworkControlPageAttachMutation.graphql.ts index eedb63fd4..c11a2064a 100644 --- a/apps/console/src/pages/organizations/frameworks/__generated__/FrameworkDetailPageAttachMutation.graphql.ts +++ b/apps/console/src/pages/organizations/frameworks/__generated__/FrameworkControlPageAttachMutation.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<81d008fc4116a78a92e2d925f8d6daa7>> + * @generated SignedSource<<1d23c8e1a8e16a9811d75d208ade0889>> * @lightSyntaxTransform * @nogrep */ @@ -14,11 +14,11 @@ export type CreateControlMeasureMappingInput = { controlId: string; measureId: string; }; -export type FrameworkDetailPageAttachMutation$variables = { +export type FrameworkControlPageAttachMutation$variables = { connections: ReadonlyArray; input: CreateControlMeasureMappingInput; }; -export type FrameworkDetailPageAttachMutation$data = { +export type FrameworkControlPageAttachMutation$data = { readonly createControlMeasureMapping: { readonly measureEdge: { readonly node: { @@ -28,9 +28,9 @@ export type FrameworkDetailPageAttachMutation$data = { }; }; }; -export type FrameworkDetailPageAttachMutation = { - response: FrameworkDetailPageAttachMutation$data; - variables: FrameworkDetailPageAttachMutation$variables; +export type FrameworkControlPageAttachMutation = { + response: FrameworkControlPageAttachMutation$data; + variables: FrameworkControlPageAttachMutation$variables; }; const node: ConcreteRequest = (function(){ @@ -66,7 +66,7 @@ return { ], "kind": "Fragment", "metadata": null, - "name": "FrameworkDetailPageAttachMutation", + "name": "FrameworkControlPageAttachMutation", "selections": [ { "alias": null, @@ -118,7 +118,7 @@ return { (v0/*: any*/) ], "kind": "Operation", - "name": "FrameworkDetailPageAttachMutation", + "name": "FrameworkControlPageAttachMutation", "selections": [ { "alias": null, @@ -187,16 +187,16 @@ return { ] }, "params": { - "cacheID": "9fcb599cd78f3108d6d71aa571d7005d", + "cacheID": "85f870b18745fcd28bac3a52c404e98f", "id": null, "metadata": {}, - "name": "FrameworkDetailPageAttachMutation", + "name": "FrameworkControlPageAttachMutation", "operationKind": "mutation", - "text": "mutation FrameworkDetailPageAttachMutation(\n $input: CreateControlMeasureMappingInput!\n) {\n createControlMeasureMapping(input: $input) {\n measureEdge {\n node {\n id\n ...LinkedMeasuresCardFragment\n }\n }\n }\n}\n\nfragment LinkedMeasuresCardFragment on Measure {\n id\n name\n state\n}\n" + "text": "mutation FrameworkControlPageAttachMutation(\n $input: CreateControlMeasureMappingInput!\n) {\n createControlMeasureMapping(input: $input) {\n measureEdge {\n node {\n id\n ...LinkedMeasuresCardFragment\n }\n }\n }\n}\n\nfragment LinkedMeasuresCardFragment on Measure {\n id\n name\n state\n}\n" } }; })(); -(node as any).hash = "d3834b8c2656d347e13189469894be61"; +(node as any).hash = "e52f26e6906aa014ad020bd6f23a75d6"; export default node; diff --git a/apps/console/src/pages/organizations/frameworks/__generated__/FrameworkDetailPageDeleteControlMutation.graphql.ts b/apps/console/src/pages/organizations/frameworks/__generated__/FrameworkControlPageDeleteControlMutation.graphql.ts similarity index 73% rename from apps/console/src/pages/organizations/frameworks/__generated__/FrameworkDetailPageDeleteControlMutation.graphql.ts rename to apps/console/src/pages/organizations/frameworks/__generated__/FrameworkControlPageDeleteControlMutation.graphql.ts index 5166e9377..c549c68c1 100644 --- a/apps/console/src/pages/organizations/frameworks/__generated__/FrameworkDetailPageDeleteControlMutation.graphql.ts +++ b/apps/console/src/pages/organizations/frameworks/__generated__/FrameworkControlPageDeleteControlMutation.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<> + * @generated SignedSource<<87a74ac3a3c2523bd65512703dc2246e>> * @lightSyntaxTransform * @nogrep */ @@ -12,18 +12,18 @@ import { ConcreteRequest } from 'relay-runtime'; export type DeleteControlInput = { controlId: string; }; -export type FrameworkDetailPageDeleteControlMutation$variables = { +export type FrameworkControlPageDeleteControlMutation$variables = { connections: ReadonlyArray; input: DeleteControlInput; }; -export type FrameworkDetailPageDeleteControlMutation$data = { +export type FrameworkControlPageDeleteControlMutation$data = { readonly deleteControl: { readonly deletedControlId: string; }; }; -export type FrameworkDetailPageDeleteControlMutation = { - response: FrameworkDetailPageDeleteControlMutation$data; - variables: FrameworkDetailPageDeleteControlMutation$variables; +export type FrameworkControlPageDeleteControlMutation = { + response: FrameworkControlPageDeleteControlMutation$data; + variables: FrameworkControlPageDeleteControlMutation$variables; }; const node: ConcreteRequest = (function(){ @@ -59,7 +59,7 @@ return { ], "kind": "Fragment", "metadata": null, - "name": "FrameworkDetailPageDeleteControlMutation", + "name": "FrameworkControlPageDeleteControlMutation", "selections": [ { "alias": null, @@ -84,7 +84,7 @@ return { (v0/*: any*/) ], "kind": "Operation", - "name": "FrameworkDetailPageDeleteControlMutation", + "name": "FrameworkControlPageDeleteControlMutation", "selections": [ { "alias": null, @@ -117,16 +117,16 @@ return { ] }, "params": { - "cacheID": "d86c7a2bd535a98d1cb7308a96c13401", + "cacheID": "f5559c3bf33da22ba6cb83fc408426e0", "id": null, "metadata": {}, - "name": "FrameworkDetailPageDeleteControlMutation", + "name": "FrameworkControlPageDeleteControlMutation", "operationKind": "mutation", - "text": "mutation FrameworkDetailPageDeleteControlMutation(\n $input: DeleteControlInput!\n) {\n deleteControl(input: $input) {\n deletedControlId\n }\n}\n" + "text": "mutation FrameworkControlPageDeleteControlMutation(\n $input: DeleteControlInput!\n) {\n deleteControl(input: $input) {\n deletedControlId\n }\n}\n" } }; })(); -(node as any).hash = "a067e712c3c51b73abac50e6c3779141"; +(node as any).hash = "c92c75d0666d2a60d3a2fcfb2959b2e9"; export default node; diff --git a/apps/console/src/pages/organizations/frameworks/__generated__/FrameworkDetailPageDetachDocumentMutation.graphql.ts b/apps/console/src/pages/organizations/frameworks/__generated__/FrameworkControlPageDetachDocumentMutation.graphql.ts similarity index 73% rename from apps/console/src/pages/organizations/frameworks/__generated__/FrameworkDetailPageDetachDocumentMutation.graphql.ts rename to apps/console/src/pages/organizations/frameworks/__generated__/FrameworkControlPageDetachDocumentMutation.graphql.ts index 8381ead9f..14d1fe584 100644 --- a/apps/console/src/pages/organizations/frameworks/__generated__/FrameworkDetailPageDetachDocumentMutation.graphql.ts +++ b/apps/console/src/pages/organizations/frameworks/__generated__/FrameworkControlPageDetachDocumentMutation.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<6f2850042c0413963bd82f90236fe220>> + * @generated SignedSource<<61b6714faa71bf0afe54a1984b719a55>> * @lightSyntaxTransform * @nogrep */ @@ -13,18 +13,18 @@ export type DeleteControlDocumentMappingInput = { controlId: string; documentId: string; }; -export type FrameworkDetailPageDetachDocumentMutation$variables = { +export type FrameworkControlPageDetachDocumentMutation$variables = { connections: ReadonlyArray; input: DeleteControlDocumentMappingInput; }; -export type FrameworkDetailPageDetachDocumentMutation$data = { +export type FrameworkControlPageDetachDocumentMutation$data = { readonly deleteControlDocumentMapping: { readonly deletedDocumentId: string; }; }; -export type FrameworkDetailPageDetachDocumentMutation = { - response: FrameworkDetailPageDetachDocumentMutation$data; - variables: FrameworkDetailPageDetachDocumentMutation$variables; +export type FrameworkControlPageDetachDocumentMutation = { + response: FrameworkControlPageDetachDocumentMutation$data; + variables: FrameworkControlPageDetachDocumentMutation$variables; }; const node: ConcreteRequest = (function(){ @@ -60,7 +60,7 @@ return { ], "kind": "Fragment", "metadata": null, - "name": "FrameworkDetailPageDetachDocumentMutation", + "name": "FrameworkControlPageDetachDocumentMutation", "selections": [ { "alias": null, @@ -85,7 +85,7 @@ return { (v0/*: any*/) ], "kind": "Operation", - "name": "FrameworkDetailPageDetachDocumentMutation", + "name": "FrameworkControlPageDetachDocumentMutation", "selections": [ { "alias": null, @@ -118,16 +118,16 @@ return { ] }, "params": { - "cacheID": "53618baa1678e3a1b496a08597bdd364", + "cacheID": "f442693991b055c11609fb7bc326f1da", "id": null, "metadata": {}, - "name": "FrameworkDetailPageDetachDocumentMutation", + "name": "FrameworkControlPageDetachDocumentMutation", "operationKind": "mutation", - "text": "mutation FrameworkDetailPageDetachDocumentMutation(\n $input: DeleteControlDocumentMappingInput!\n) {\n deleteControlDocumentMapping(input: $input) {\n deletedDocumentId\n }\n}\n" + "text": "mutation FrameworkControlPageDetachDocumentMutation(\n $input: DeleteControlDocumentMappingInput!\n) {\n deleteControlDocumentMapping(input: $input) {\n deletedDocumentId\n }\n}\n" } }; })(); -(node as any).hash = "233b182104744a67a21d21d60b6df67a"; +(node as any).hash = "8c20b51090f69e932bc4801d88a6eaa6"; export default node; diff --git a/apps/console/src/pages/organizations/frameworks/__generated__/FrameworkDetailPageDetachMutation.graphql.ts b/apps/console/src/pages/organizations/frameworks/__generated__/FrameworkControlPageDetachMutation.graphql.ts similarity index 75% rename from apps/console/src/pages/organizations/frameworks/__generated__/FrameworkDetailPageDetachMutation.graphql.ts rename to apps/console/src/pages/organizations/frameworks/__generated__/FrameworkControlPageDetachMutation.graphql.ts index 953e08a4e..632e7040b 100644 --- a/apps/console/src/pages/organizations/frameworks/__generated__/FrameworkDetailPageDetachMutation.graphql.ts +++ b/apps/console/src/pages/organizations/frameworks/__generated__/FrameworkControlPageDetachMutation.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<> + * @generated SignedSource<<40a72ad7f2eb9fc26bf12c7f4ce731d7>> * @lightSyntaxTransform * @nogrep */ @@ -13,18 +13,18 @@ export type DeleteControlMeasureMappingInput = { controlId: string; measureId: string; }; -export type FrameworkDetailPageDetachMutation$variables = { +export type FrameworkControlPageDetachMutation$variables = { connections: ReadonlyArray; input: DeleteControlMeasureMappingInput; }; -export type FrameworkDetailPageDetachMutation$data = { +export type FrameworkControlPageDetachMutation$data = { readonly deleteControlMeasureMapping: { readonly deletedMeasureId: string; }; }; -export type FrameworkDetailPageDetachMutation = { - response: FrameworkDetailPageDetachMutation$data; - variables: FrameworkDetailPageDetachMutation$variables; +export type FrameworkControlPageDetachMutation = { + response: FrameworkControlPageDetachMutation$data; + variables: FrameworkControlPageDetachMutation$variables; }; const node: ConcreteRequest = (function(){ @@ -60,7 +60,7 @@ return { ], "kind": "Fragment", "metadata": null, - "name": "FrameworkDetailPageDetachMutation", + "name": "FrameworkControlPageDetachMutation", "selections": [ { "alias": null, @@ -85,7 +85,7 @@ return { (v0/*: any*/) ], "kind": "Operation", - "name": "FrameworkDetailPageDetachMutation", + "name": "FrameworkControlPageDetachMutation", "selections": [ { "alias": null, @@ -118,16 +118,16 @@ return { ] }, "params": { - "cacheID": "0bede5dc29caa4e2aba9b0dd96e58014", + "cacheID": "ce39a8bbb51cd7437dbc08612b60a214", "id": null, "metadata": {}, - "name": "FrameworkDetailPageDetachMutation", + "name": "FrameworkControlPageDetachMutation", "operationKind": "mutation", - "text": "mutation FrameworkDetailPageDetachMutation(\n $input: DeleteControlMeasureMappingInput!\n) {\n deleteControlMeasureMapping(input: $input) {\n deletedMeasureId\n }\n}\n" + "text": "mutation FrameworkControlPageDetachMutation(\n $input: DeleteControlMeasureMappingInput!\n) {\n deleteControlMeasureMapping(input: $input) {\n deletedMeasureId\n }\n}\n" } }; })(); -(node as any).hash = "96945bf8e909e7425af2297f7ee6ea0a"; +(node as any).hash = "c62748aaf06f0050732be91ae341ae73"; export default node; diff --git a/apps/console/src/pages/organizations/frameworks/__generated__/FrameworkDetailPageFragment.graphql.ts b/apps/console/src/pages/organizations/frameworks/__generated__/FrameworkDetailPageFragment.graphql.ts index e91bd5fec..86d97ace4 100644 --- a/apps/console/src/pages/organizations/frameworks/__generated__/FrameworkDetailPageFragment.graphql.ts +++ b/apps/console/src/pages/organizations/frameworks/__generated__/FrameworkDetailPageFragment.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<775e1361b15523efe196b9edbd406f5a>> + * @generated SignedSource<<6b38711542a158ba9424c744d217bed5>> * @lightSyntaxTransform * @nogrep */ @@ -15,29 +15,9 @@ export type FrameworkDetailPageFragment$data = { readonly __id: string; readonly edges: ReadonlyArray<{ readonly node: { - readonly description: string; - readonly documents: { - readonly __id: string; - readonly edges: ReadonlyArray<{ - readonly node: { - readonly id: string; - readonly " $fragmentSpreads": FragmentRefs<"LinkedDocumentsCardFragment">; - }; - }>; - }; readonly id: string; - readonly measures: { - readonly __id: string; - readonly edges: ReadonlyArray<{ - readonly node: { - readonly id: string; - readonly " $fragmentSpreads": FragmentRefs<"LinkedMeasuresCardFragment">; - }; - }>; - }; readonly name: string; readonly sectionTitle: string; - readonly " $fragmentSpreads": FragmentRefs<"FrameworkControlDialogFragment">; }; }>; }; @@ -53,97 +33,34 @@ export type FrameworkDetailPageFragment$key = { const node: ReaderFragment = (function(){ var v0 = { - "count": null, - "cursor": null, - "direction": "forward", - "path": null -}, -v1 = { "alias": null, "args": null, "kind": "ScalarField", "name": "id", "storageKey": null }, -v2 = { +v1 = { "alias": null, "args": null, "kind": "ScalarField", "name": "name", "storageKey": null -}, -v3 = { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "description", - "storageKey": null -}, -v4 = { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "__typename", - "storageKey": null -}, -v5 = { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "cursor", - "storageKey": null -}, -v6 = { - "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": "ClientExtension", - "selections": [ - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "__id", - "storageKey": null - } - ] }; return { "argumentDefinitions": [], "kind": "Fragment", - "metadata": { - "connection": [ - (v0/*: any*/), - (v0/*: any*/) - ] - }, + "metadata": null, "name": "FrameworkDetailPageFragment", "selections": [ + (v0/*: any*/), (v1/*: any*/), - (v2/*: any*/), - (v3/*: any*/), + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "description", + "storageKey": null + }, { "alias": null, "args": [ @@ -174,7 +91,7 @@ return { "name": "node", "plural": false, "selections": [ - (v1/*: any*/), + (v0/*: any*/), { "alias": null, "args": null, @@ -182,106 +99,25 @@ return { "name": "sectionTitle", "storageKey": null }, - (v2/*: any*/), - (v3/*: any*/), - { - "args": null, - "kind": "FragmentSpread", - "name": "FrameworkControlDialogFragment" - }, - { - "alias": "measures", - "args": null, - "concreteType": "MeasureConnection", - "kind": "LinkedField", - "name": "__FrameworkDetailPage_measures_connection", - "plural": false, - "selections": [ - { - "alias": null, - "args": null, - "concreteType": "MeasureEdge", - "kind": "LinkedField", - "name": "edges", - "plural": true, - "selections": [ - { - "alias": null, - "args": null, - "concreteType": "Measure", - "kind": "LinkedField", - "name": "node", - "plural": false, - "selections": [ - (v1/*: any*/), - { - "args": null, - "kind": "FragmentSpread", - "name": "LinkedMeasuresCardFragment" - }, - (v4/*: any*/) - ], - "storageKey": null - }, - (v5/*: any*/) - ], - "storageKey": null - }, - (v6/*: any*/), - (v7/*: any*/) - ], - "storageKey": null - }, - { - "alias": "documents", - "args": null, - "concreteType": "DocumentConnection", - "kind": "LinkedField", - "name": "__FrameworkDetailPage_documents_connection", - "plural": false, - "selections": [ - { - "alias": null, - "args": null, - "concreteType": "DocumentEdge", - "kind": "LinkedField", - "name": "edges", - "plural": true, - "selections": [ - { - "alias": null, - "args": null, - "concreteType": "Document", - "kind": "LinkedField", - "name": "node", - "plural": false, - "selections": [ - (v1/*: any*/), - { - "args": null, - "kind": "FragmentSpread", - "name": "LinkedDocumentsCardFragment" - }, - (v4/*: any*/) - ], - "storageKey": null - }, - (v5/*: any*/) - ], - "storageKey": null - }, - (v6/*: any*/), - (v7/*: any*/) - ], - "storageKey": null - } + (v1/*: any*/) ], "storageKey": null } ], "storageKey": null }, - (v7/*: any*/) + { + "kind": "ClientExtension", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__id", + "storageKey": null + } + ] + } ], "storageKey": "controls(first:100)" } @@ -291,6 +127,6 @@ return { }; })(); -(node as any).hash = "b70c5b2090c5db158c44f0b9fcc0f9bc"; +(node as any).hash = "e95f82575c72a8b95c87ecd3ffc676ed"; export default node; diff --git a/apps/console/src/routes.tsx b/apps/console/src/routes.tsx index 28f4d0a09..16e594fef 100644 --- a/apps/console/src/routes.tsx +++ b/apps/console/src/routes.tsx @@ -165,7 +165,11 @@ function routeTransformer({ useCleanup(dispose, 1000); - return ; + return ( + : null}> + + + ); }, }; } diff --git a/apps/console/src/routes/documentsRoutes.ts b/apps/console/src/routes/documentsRoutes.ts index 0fcfa98fe..1c404398d 100644 --- a/apps/console/src/routes/documentsRoutes.ts +++ b/apps/console/src/routes/documentsRoutes.ts @@ -7,6 +7,7 @@ import { documentNodeQuery } from "/hooks/graph/DocumentGraph"; import { PageSkeleton } from "/components/skeletons/PageSkeleton"; import { redirect } from "react-router"; import { lazy } from "@probo/react-lazy"; +import { LinkCardSkeleton } from "/components/skeletons/LinkCardSkeleton"; export const documentsRoutes = [ { @@ -38,6 +39,7 @@ export const documentsRoutes = [ }, { path: "description", + fallback: LinkCardSkeleton, Component: lazy( () => import( @@ -47,6 +49,7 @@ export const documentsRoutes = [ }, { path: "controls", + fallback: LinkCardSkeleton, Component: lazy( () => import("../pages/organizations/documents/tabs/DocumentControlsTab") diff --git a/apps/console/src/routes/frameworkRoutes.ts b/apps/console/src/routes/frameworkRoutes.ts index da0d765ca..87a37a2b6 100644 --- a/apps/console/src/routes/frameworkRoutes.ts +++ b/apps/console/src/routes/frameworkRoutes.ts @@ -4,10 +4,12 @@ import { relayEnvironment } from "/providers/RelayProviders"; import { frameworksQuery, frameworkNodeQuery, + frameworkControlNodeQuery, } from "/hooks/graph/FrameworkGraph"; import type { AppRoute } from "/routes"; import { Fragment } from "react"; import { lazy } from "@probo/react-lazy"; +import { ControlSkeleton } from "../components/skeletons/ControlSkeleton"; export const frameworkRoutes = [ { @@ -34,7 +36,12 @@ export const frameworkRoutes = [ }, { path: "controls/:controlId", - Component: Fragment, + fallback: ControlSkeleton, + queryLoader: ({ controlId }) => + loadQuery(relayEnvironment, frameworkControlNodeQuery, { controlId }), + Component: lazy( + () => import("/pages/organizations/frameworks/FrameworkControlPage") + ), }, ], }, diff --git a/apps/console/src/routes/measureRoutes.ts b/apps/console/src/routes/measureRoutes.ts index f817cbed1..f5559949b 100644 --- a/apps/console/src/routes/measureRoutes.ts +++ b/apps/console/src/routes/measureRoutes.ts @@ -6,6 +6,7 @@ import { measureNodeQuery, measuresQuery } from "/hooks/graph/MeasureGraph"; import { PageSkeleton } from "/components/skeletons/PageSkeleton"; import { redirect } from "react-router"; import { lazy } from "@probo/react-lazy"; +import { LinkCardSkeleton } from "/components/skeletons/LinkCardSkeleton"; export const measureRoutes = [ { @@ -39,18 +40,21 @@ export const measureRoutes = [ }, { path: "risks", + fallback: LinkCardSkeleton, Component: lazy( () => import("/pages/organizations/measures/tabs/MeasureRisksTab.tsx") ), }, { path: "tasks", + fallback: LinkCardSkeleton, Component: lazy( () => import("/pages/organizations/measures/tabs/MeasureTasksTab.tsx") ), }, { path: "controls", + fallback: LinkCardSkeleton, Component: lazy( () => import("/pages/organizations/measures/tabs/MeasureControlsTab.tsx") @@ -58,6 +62,7 @@ export const measureRoutes = [ }, { path: "evidences", + fallback: LinkCardSkeleton, Component: lazy( () => import("/pages/organizations/measures/tabs/MeasureEvidencesTab.tsx") diff --git a/apps/console/src/routes/peopleRoutes.ts b/apps/console/src/routes/peopleRoutes.ts index 08a7b1025..8ff3ac174 100644 --- a/apps/console/src/routes/peopleRoutes.ts +++ b/apps/console/src/routes/peopleRoutes.ts @@ -7,6 +7,7 @@ import { paginatedPeopleQuery, peopleNodeQuery, } from "/hooks/graph/PeopleGraph"; +import { LinkCardSkeleton } from "/components/skeletons/LinkCardSkeleton"; export const peopleRoutes = [ { @@ -27,18 +28,21 @@ export const peopleRoutes = [ children: [ { path: "tasks", + fallback: LinkCardSkeleton, Component: lazy( () => import("/pages/organizations/people/tabs/PeopleTasksTab") ), }, { path: "role", + fallback: LinkCardSkeleton, Component: lazy( () => import("/pages/organizations/people/tabs/PeopleRoleTab") ), }, { path: "profile", + fallback: LinkCardSkeleton, Component: lazy( () => import("/pages/organizations/people/tabs/PeopleProfileTab") ), diff --git a/apps/console/src/routes/riskRoutes.ts b/apps/console/src/routes/riskRoutes.ts index 4598e7723..8b93ef30a 100644 --- a/apps/console/src/routes/riskRoutes.ts +++ b/apps/console/src/routes/riskRoutes.ts @@ -7,6 +7,7 @@ import { riskNodeQuery, risksQuery } from "/hooks/graph/RiskGraph"; import { PageSkeleton } from "/components/skeletons/PageSkeleton"; import { redirect } from "react-router"; import { lazy } from "@probo/react-lazy"; +import { LinkCardSkeleton } from "/components/skeletons/LinkCardSkeleton"; export const riskRoutes = [ { @@ -32,24 +33,28 @@ export const riskRoutes = [ }, { path: "overview", + fallback: LinkCardSkeleton, Component: lazy( () => import("/pages/organizations/risks/tabs/RiskOverviewTab.tsx") ), }, { path: "measures", + fallback: LinkCardSkeleton, Component: lazy( () => import("/pages/organizations/risks/tabs/RiskMeasuresTab.tsx") ), }, { path: "documents", + fallback: LinkCardSkeleton, Component: lazy( () => import("/pages/organizations/risks/tabs/RiskDocumentsTab.tsx") ), }, { path: "controls", + fallback: LinkCardSkeleton, Component: lazy( () => import("/pages/organizations/risks/tabs/RiskControlsTab.tsx") ), diff --git a/apps/console/src/routes/vendorRoutes.ts b/apps/console/src/routes/vendorRoutes.ts index 797db13da..25e5aab38 100644 --- a/apps/console/src/routes/vendorRoutes.ts +++ b/apps/console/src/routes/vendorRoutes.ts @@ -4,6 +4,7 @@ import type { AppRoute } from "/routes.tsx"; import { relayEnvironment } from "/providers/RelayProviders"; import { PageSkeleton } from "/components/skeletons/PageSkeleton"; import { vendorNodeQuery, vendorsQuery } from "/hooks/graph/VendorGraph"; +import { LinkCardSkeleton } from "/components/skeletons/LinkCardSkeleton"; export const vendorRoutes = [ { @@ -27,12 +28,14 @@ export const vendorRoutes = [ children: [ { path: "overview", + fallback: LinkCardSkeleton, Component: lazy( () => import("../pages/organizations/vendors/tabs/VendorOverviewTab") ), }, { path: "certifications", + fallback: LinkCardSkeleton, Component: lazy( () => import( @@ -42,6 +45,7 @@ export const vendorRoutes = [ }, { path: "compliance", + fallback: LinkCardSkeleton, Component: lazy( () => import("../pages/organizations/vendors/tabs/VendorComplianceTab") @@ -49,6 +53,7 @@ export const vendorRoutes = [ }, { path: "risks", + fallback: LinkCardSkeleton, Component: lazy( () => import(