Add contols audits

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
Sacha Al Himdani
2025-08-14 13:42:47 +02:00
parent 215e97d9f5
commit 55d488edac
22 changed files with 3762 additions and 21 deletions

View File

@@ -28,6 +28,7 @@ import {
} from "@probo/ui";
import { useTranslate } from "@probo/i18n";
import { useOrganizationId } from "/hooks/useOrganizationId";
import { FrameworkLogo } from "/components/FrameworkLogo";
import { ControlledField } from "/components/form/ControlledField";
import { useFormWithSchema } from "/hooks/useFormWithSchema";
import z from "zod";
@@ -135,7 +136,10 @@ export default function AuditDetailsPage(props: Props) {
<div className="flex justify-between items-start">
<div className="flex items-center gap-4">
<div className="text-2xl">{auditEntry.framework?.name}</div>
<div className="flex items-center gap-3">
<FrameworkLogo name={auditEntry.framework?.name || ""} />
<div className="text-2xl">{auditEntry.framework?.name}</div>
</div>
<Badge variant={getAuditStateVariant(auditEntry.state || "NOT_STARTED")}>
{getAuditStateLabel(__, auditEntry.state || "NOT_STARTED")}
</Badge>

View File

@@ -17,6 +17,7 @@ import { LinkedMeasuresCard } from "/components/measures/LinkedMeasuresCard";
import { useNavigate, useOutletContext } from "react-router";
import { useOrganizationId } from "/hooks/useOrganizationId";
import { LinkedDocumentsCard } from "/components/documents/LinkedDocumentsCard";
import { LinkedAuditsCard } from "/components/audits/LinkedAuditsCard";
import { FrameworkControlDialog } from "./dialogs/FrameworkControlDialog";
import { promisifyMutation } from "@probo/helpers";
import type { FrameworkGraphControlNodeQuery } from "/hooks/graph/__generated__/FrameworkGraphControlNodeQuery.graphql";
@@ -77,6 +78,33 @@ const detachDocumentMutation = graphql`
}
`;
const attachAuditMutation = graphql`
mutation FrameworkControlPageAttachAuditMutation(
$input: CreateControlAuditMappingInput!
$connections: [ID!]!
) {
createControlAuditMapping(input: $input) {
auditEdge @prependEdge(connections: $connections) {
node {
id
...LinkedAuditsCardFragment
}
}
}
}
`;
const detachAuditMutation = graphql`
mutation FrameworkControlPageDetachAuditMutation(
$input: DeleteControlAuditMappingInput!
$connections: [ID!]!
) {
deleteControlAuditMapping(input: $input) {
deletedAuditId @deleteEdge(connections: $connections)
}
}
`;
const deleteControlMutation = graphql`
mutation FrameworkControlPageDeleteControlMutation(
$input: DeleteControlInput!
@@ -118,6 +146,8 @@ export default function FrameworkControlPage({ queryRef }: Props) {
const [attachDocument, isAttachingDocument] = useMutation(
attachDocumentMutation
);
const [detachAudit, isDetachingAudit] = useMutation(detachAuditMutation);
const [attachAudit, isAttachingAudit] = useMutation(attachAuditMutation);
const [deleteControl] = useMutation(deleteControlMutation);
const onDelete = () => {
@@ -204,7 +234,16 @@ export default function FrameworkControlPage({ queryRef }: Props) {
onAttach={attachDocument}
onDetach={detachDocument}
disabled={isAttachingDocument || isDetachingDocument}
/>
/>
<LinkedAuditsCard
variant="card"
audits={control.audits?.edges.map((edge) => edge.node) ?? []}
params={{ controlId: control.id }}
connectionId={control.audits?.__id!}
onAttach={attachAudit}
onDetach={detachAudit}
disabled={isAttachingAudit || isDetachingAudit}
/>
</div>
</div>
);

View File

@@ -0,0 +1,237 @@
/**
* @generated SignedSource<<c1f987833f73f19a66f044b28688f1dd>>
* @lightSyntaxTransform
* @nogrep
*/
/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
import { ConcreteRequest } from 'relay-runtime';
import { FragmentRefs } from "relay-runtime";
export type CreateControlAuditMappingInput = {
auditId: string;
controlId: string;
};
export type FrameworkControlPageAttachAuditMutation$variables = {
connections: ReadonlyArray<string>;
input: CreateControlAuditMappingInput;
};
export type FrameworkControlPageAttachAuditMutation$data = {
readonly createControlAuditMapping: {
readonly auditEdge: {
readonly node: {
readonly id: string;
readonly " $fragmentSpreads": FragmentRefs<"LinkedAuditsCardFragment">;
};
};
};
};
export type FrameworkControlPageAttachAuditMutation = {
response: FrameworkControlPageAttachAuditMutation$data;
variables: FrameworkControlPageAttachAuditMutation$variables;
};
const node: ConcreteRequest = (function(){
var v0 = {
"defaultValue": null,
"kind": "LocalArgument",
"name": "connections"
},
v1 = {
"defaultValue": null,
"kind": "LocalArgument",
"name": "input"
},
v2 = [
{
"kind": "Variable",
"name": "input",
"variableName": "input"
}
],
v3 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "id",
"storageKey": null
},
v4 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "name",
"storageKey": null
};
return {
"fragment": {
"argumentDefinitions": [
(v0/*: any*/),
(v1/*: any*/)
],
"kind": "Fragment",
"metadata": null,
"name": "FrameworkControlPageAttachAuditMutation",
"selections": [
{
"alias": null,
"args": (v2/*: any*/),
"concreteType": "CreateControlAuditMappingPayload",
"kind": "LinkedField",
"name": "createControlAuditMapping",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "AuditEdge",
"kind": "LinkedField",
"name": "auditEdge",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "Audit",
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
(v3/*: any*/),
{
"args": null,
"kind": "FragmentSpread",
"name": "LinkedAuditsCardFragment"
}
],
"storageKey": null
}
],
"storageKey": null
}
],
"storageKey": null
}
],
"type": "Mutation",
"abstractKey": null
},
"kind": "Request",
"operation": {
"argumentDefinitions": [
(v1/*: any*/),
(v0/*: any*/)
],
"kind": "Operation",
"name": "FrameworkControlPageAttachAuditMutation",
"selections": [
{
"alias": null,
"args": (v2/*: any*/),
"concreteType": "CreateControlAuditMappingPayload",
"kind": "LinkedField",
"name": "createControlAuditMapping",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "AuditEdge",
"kind": "LinkedField",
"name": "auditEdge",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "Audit",
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
(v3/*: any*/),
(v4/*: any*/),
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "createdAt",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "state",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "validFrom",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "validUntil",
"storageKey": null
},
{
"alias": null,
"args": null,
"concreteType": "Framework",
"kind": "LinkedField",
"name": "framework",
"plural": false,
"selections": [
(v3/*: any*/),
(v4/*: any*/)
],
"storageKey": null
}
],
"storageKey": null
}
],
"storageKey": null
},
{
"alias": null,
"args": null,
"filters": null,
"handle": "prependEdge",
"key": "",
"kind": "LinkedHandle",
"name": "auditEdge",
"handleArgs": [
{
"kind": "Variable",
"name": "connections",
"variableName": "connections"
}
]
}
],
"storageKey": null
}
]
},
"params": {
"cacheID": "23afa68a91d9ae6630123a4ff693a0ec",
"id": null,
"metadata": {},
"name": "FrameworkControlPageAttachAuditMutation",
"operationKind": "mutation",
"text": "mutation FrameworkControlPageAttachAuditMutation(\n $input: CreateControlAuditMappingInput!\n) {\n createControlAuditMapping(input: $input) {\n auditEdge {\n node {\n id\n ...LinkedAuditsCardFragment\n }\n }\n }\n}\n\nfragment LinkedAuditsCardFragment on Audit {\n id\n name\n createdAt\n state\n validFrom\n validUntil\n framework {\n id\n name\n }\n}\n"
}
};
})();
(node as any).hash = "0a9f52be5fb551ec55667b7d777391de";
export default node;

View File

@@ -0,0 +1,133 @@
/**
* @generated SignedSource<<f3f6c7f7f6e09f53b111e39ce4176b1c>>
* @lightSyntaxTransform
* @nogrep
*/
/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
import { ConcreteRequest } from 'relay-runtime';
export type DeleteControlAuditMappingInput = {
auditId: string;
controlId: string;
};
export type FrameworkControlPageDetachAuditMutation$variables = {
connections: ReadonlyArray<string>;
input: DeleteControlAuditMappingInput;
};
export type FrameworkControlPageDetachAuditMutation$data = {
readonly deleteControlAuditMapping: {
readonly deletedAuditId: string;
};
};
export type FrameworkControlPageDetachAuditMutation = {
response: FrameworkControlPageDetachAuditMutation$data;
variables: FrameworkControlPageDetachAuditMutation$variables;
};
const node: ConcreteRequest = (function(){
var v0 = {
"defaultValue": null,
"kind": "LocalArgument",
"name": "connections"
},
v1 = {
"defaultValue": null,
"kind": "LocalArgument",
"name": "input"
},
v2 = [
{
"kind": "Variable",
"name": "input",
"variableName": "input"
}
],
v3 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "deletedAuditId",
"storageKey": null
};
return {
"fragment": {
"argumentDefinitions": [
(v0/*: any*/),
(v1/*: any*/)
],
"kind": "Fragment",
"metadata": null,
"name": "FrameworkControlPageDetachAuditMutation",
"selections": [
{
"alias": null,
"args": (v2/*: any*/),
"concreteType": "DeleteControlAuditMappingPayload",
"kind": "LinkedField",
"name": "deleteControlAuditMapping",
"plural": false,
"selections": [
(v3/*: any*/)
],
"storageKey": null
}
],
"type": "Mutation",
"abstractKey": null
},
"kind": "Request",
"operation": {
"argumentDefinitions": [
(v1/*: any*/),
(v0/*: any*/)
],
"kind": "Operation",
"name": "FrameworkControlPageDetachAuditMutation",
"selections": [
{
"alias": null,
"args": (v2/*: any*/),
"concreteType": "DeleteControlAuditMappingPayload",
"kind": "LinkedField",
"name": "deleteControlAuditMapping",
"plural": false,
"selections": [
(v3/*: any*/),
{
"alias": null,
"args": null,
"filters": null,
"handle": "deleteEdge",
"key": "",
"kind": "ScalarHandle",
"name": "deletedAuditId",
"handleArgs": [
{
"kind": "Variable",
"name": "connections",
"variableName": "connections"
}
]
}
],
"storageKey": null
}
]
},
"params": {
"cacheID": "0ec6a285e6ffd0057602e30ef8411e16",
"id": null,
"metadata": {},
"name": "FrameworkControlPageDetachAuditMutation",
"operationKind": "mutation",
"text": "mutation FrameworkControlPageDetachAuditMutation(\n $input: DeleteControlAuditMappingInput!\n) {\n deleteControlAuditMapping(input: $input) {\n deletedAuditId\n }\n}\n"
}
};
})();
(node as any).hash = "3c800764c7d5801bd228e8a61625bd75";
export default node;