@@ -88,7 +88,10 @@ import { MesureViewAssignTaskMutation as MesureViewAssignTaskMutationType } from
|
||||
import { MesureViewUnassignTaskMutation as MesureViewUnassignTaskMutationType } from "./__generated__/MesureViewUnassignTaskMutation.graphql";
|
||||
import { MesureViewUpdateMesureStateMutation as MesureViewUpdateMesureStateMutationType } from "./__generated__/MesureViewUpdateMesureStateMutation.graphql";
|
||||
import { MesureViewQuery as MesureViewQueryType } from "./__generated__/MesureViewQuery.graphql";
|
||||
import { MesureViewOrganizationQuery$data } from "./__generated__/MesureViewOrganizationQuery.graphql";
|
||||
import {
|
||||
MesureViewOrganizationQuery,
|
||||
MesureViewOrganizationQuery$data,
|
||||
} from "./__generated__/MesureViewOrganizationQuery.graphql";
|
||||
import {
|
||||
MesureViewFrameworksQuery,
|
||||
MesureViewFrameworksQuery$data,
|
||||
@@ -99,6 +102,10 @@ import {
|
||||
} from "./__generated__/MesureViewLinkedControlsQuery.graphql";
|
||||
import { MesureViewCreateEvidenceMutation as MesureViewCreateEvidenceMutationType } from "./__generated__/MesureViewCreateEvidenceMutation.graphql";
|
||||
import { MesureViewFulfillEvidenceMutation as MesureViewFulfillEvidenceMutationType } from "./__generated__/MesureViewFulfillEvidenceMutation.graphql";
|
||||
import { MesureViewCreateControlMappingMutation } from "./__generated__/MesureViewCreateControlMappingMutation.graphql";
|
||||
import { MesureViewDeleteControlMappingMutation } from "./__generated__/MesureViewDeleteControlMappingMutation.graphql";
|
||||
import { MesureViewRisksQuery$data } from "./__generated__/MesureViewRisksQuery.graphql";
|
||||
import { MesureViewRisksQuery } from "./__generated__/MesureViewRisksQuery.graphql";
|
||||
|
||||
// Function to format ISO8601 duration to human-readable format
|
||||
const formatDuration = (isoDuration: string): string => {
|
||||
@@ -448,6 +455,10 @@ const mesureRisksQuery = graphql`
|
||||
description
|
||||
inherentLikelihood
|
||||
inherentImpact
|
||||
inherentSeverity
|
||||
residualLikelihood
|
||||
residualImpact
|
||||
residualSeverity
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
@@ -458,55 +469,6 @@ const mesureRisksQuery = graphql`
|
||||
}
|
||||
`;
|
||||
|
||||
// Define type for framework structure
|
||||
type FrameworkEdge = {
|
||||
node: {
|
||||
id: string;
|
||||
name: string;
|
||||
controls: {
|
||||
edges: Array<{
|
||||
node: {
|
||||
id: string;
|
||||
referenceId: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
};
|
||||
}>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
type ControlNode = {
|
||||
id: string;
|
||||
referenceId: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
};
|
||||
|
||||
// Define Risk type
|
||||
type RiskNode = {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
likelihood: number;
|
||||
impact: number;
|
||||
severity: number;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
};
|
||||
|
||||
// Define the risks data type
|
||||
type MesureRisksData = {
|
||||
mesure?: {
|
||||
id: string;
|
||||
risks?: {
|
||||
edges: Array<{
|
||||
node: RiskNode;
|
||||
}>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
function MesureViewContent({
|
||||
queryRef,
|
||||
}: {
|
||||
@@ -548,18 +510,22 @@ function MesureViewContent({
|
||||
const [isUnlinkingControl, setIsUnlinkingControl] = useState(false);
|
||||
|
||||
// Create mutation hooks for control mapping
|
||||
const [commitCreateControlMapping] = useMutation(
|
||||
createControlMappingMutation
|
||||
);
|
||||
const [commitDeleteControlMapping] = useMutation(
|
||||
deleteControlMappingMutation
|
||||
);
|
||||
const [commitCreateControlMapping] =
|
||||
useMutation<MesureViewCreateControlMappingMutation>(
|
||||
createControlMappingMutation
|
||||
);
|
||||
const [commitDeleteControlMapping] =
|
||||
useMutation<MesureViewDeleteControlMappingMutation>(
|
||||
deleteControlMappingMutation
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (organizationId) {
|
||||
fetchQuery(environment, organizationQuery, { organizationId }).subscribe({
|
||||
next: (data: unknown) => {
|
||||
setOrganizationData(data as MesureViewOrganizationQuery$data);
|
||||
fetchQuery<MesureViewOrganizationQuery>(environment, organizationQuery, {
|
||||
organizationId,
|
||||
}).subscribe({
|
||||
next: (data) => {
|
||||
setOrganizationData(data);
|
||||
},
|
||||
error: (error: Error) => {
|
||||
console.error("Error fetching organization:", error);
|
||||
@@ -571,9 +537,13 @@ function MesureViewContent({
|
||||
// Load linked controls when component mounts
|
||||
useEffect(() => {
|
||||
if (mesureId) {
|
||||
fetchQuery(environment, linkedControlsQuery, { mesureId }).subscribe({
|
||||
next: (data: unknown) => {
|
||||
setLinkedControlsData(data as MesureViewLinkedControlsQuery$data);
|
||||
fetchQuery<MesureViewLinkedControlsQuery>(
|
||||
environment,
|
||||
linkedControlsQuery,
|
||||
{ mesureId }
|
||||
).subscribe({
|
||||
next: (data) => {
|
||||
setLinkedControlsData(data);
|
||||
},
|
||||
error: (error: Error) => {
|
||||
console.error("Error fetching linked controls:", error);
|
||||
@@ -583,14 +553,18 @@ function MesureViewContent({
|
||||
}, [environment, mesureId]);
|
||||
|
||||
// Add state for risks data
|
||||
const [risksData, setRisksData] = useState<MesureRisksData | null>(null);
|
||||
const [risksData, setRisksData] = useState<MesureViewRisksQuery$data | null>(
|
||||
null
|
||||
);
|
||||
|
||||
// Load risks data when component mounts
|
||||
useEffect(() => {
|
||||
if (mesureId) {
|
||||
fetchQuery(environment, mesureRisksQuery, { mesureId }).subscribe({
|
||||
next: (data: unknown) => {
|
||||
setRisksData(data as MesureRisksData);
|
||||
fetchQuery<MesureViewRisksQuery>(environment, mesureRisksQuery, {
|
||||
mesureId,
|
||||
}).subscribe({
|
||||
next: (data) => {
|
||||
setRisksData(data);
|
||||
},
|
||||
error: (error: Error) => {
|
||||
console.error("Error fetching risks:", error);
|
||||
@@ -1626,11 +1600,15 @@ function MesureViewContent({
|
||||
},
|
||||
complete: () => {
|
||||
// Fetch already linked controls for this mesure
|
||||
fetchQuery(environment, linkedControlsQuery, {
|
||||
mesureId,
|
||||
}).subscribe({
|
||||
next: (data: unknown) => {
|
||||
setLinkedControlsData(data as MesureViewLinkedControlsQuery$data);
|
||||
fetchQuery<MesureViewLinkedControlsQuery>(
|
||||
environment,
|
||||
linkedControlsQuery,
|
||||
{
|
||||
mesureId,
|
||||
}
|
||||
).subscribe({
|
||||
next: (data) => {
|
||||
setLinkedControlsData(data);
|
||||
setIsLoadingControls(false);
|
||||
},
|
||||
error: (error: Error) => {
|
||||
@@ -1660,11 +1638,10 @@ function MesureViewContent({
|
||||
if (!frameworksData?.organization?.frameworks?.edges) return [];
|
||||
|
||||
// Get controls from the selected framework
|
||||
const frameworks = frameworksData.organization.frameworks
|
||||
.edges as Array<FrameworkEdge>;
|
||||
const frameworks = frameworksData.organization.frameworks.edges;
|
||||
if (selectedFrameworkId) {
|
||||
const selectedFramework = frameworks.find(
|
||||
(edge: FrameworkEdge) => edge.node.id === selectedFrameworkId
|
||||
(edge) => edge.node.id === selectedFrameworkId
|
||||
);
|
||||
|
||||
if (selectedFramework?.node?.controls?.edges) {
|
||||
@@ -1673,7 +1650,7 @@ function MesureViewContent({
|
||||
}
|
||||
|
||||
// If no framework is selected or it doesn't have controls, return controls from all frameworks
|
||||
return frameworks.flatMap((framework: FrameworkEdge) =>
|
||||
return frameworks.flatMap((framework) =>
|
||||
framework.node.controls.edges.map((edge) => edge.node)
|
||||
);
|
||||
}, [frameworksData, selectedFrameworkId]);
|
||||
@@ -1688,9 +1665,7 @@ function MesureViewContent({
|
||||
const isControlLinked = useCallback(
|
||||
(controlId: string) => {
|
||||
const linkedControls = getLinkedControls();
|
||||
return linkedControls.some(
|
||||
(control: ControlNode) => control.id === controlId
|
||||
);
|
||||
return linkedControls.some((control) => control.id === controlId);
|
||||
},
|
||||
[getLinkedControls]
|
||||
);
|
||||
@@ -1824,7 +1799,7 @@ function MesureViewContent({
|
||||
|
||||
const lowerQuery = controlSearchQuery.toLowerCase();
|
||||
return controls.filter(
|
||||
(control: ControlNode) =>
|
||||
(control) =>
|
||||
control.referenceId.toLowerCase().includes(lowerQuery) ||
|
||||
control.name.toLowerCase().includes(lowerQuery) ||
|
||||
(control.description &&
|
||||
@@ -1849,7 +1824,7 @@ function MesureViewContent({
|
||||
};
|
||||
|
||||
// Format likelihood as text
|
||||
const formatlikelihood = (value: number): string => {
|
||||
const formatLikelihood = (value: number): string => {
|
||||
if (value <= 0.1) return "Very Low";
|
||||
if (value <= 0.3) return "Low";
|
||||
if (value <= 0.5) return "Medium";
|
||||
@@ -2144,7 +2119,7 @@ function MesureViewContent({
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{getLinkedControls().map((control: ControlNode) => (
|
||||
{getLinkedControls().map((control) => (
|
||||
<tr
|
||||
key={control.id}
|
||||
className="border-b hover:bg-invert-bg"
|
||||
@@ -2832,21 +2807,21 @@ function MesureViewContent({
|
||||
</td>
|
||||
<td className="py-3 px-4">
|
||||
<div className="bg-blue-100 text-blue-800 px-2 py-0.5 rounded-full text-xs inline-block">
|
||||
{formatlikelihood(node.likelihood)}
|
||||
{formatLikelihood(node.inherentLikelihood)}
|
||||
</div>
|
||||
</td>
|
||||
<td className="py-3 px-4">
|
||||
<div className="bg-purple-100 text-purple-800 px-2 py-0.5 rounded-full text-xs inline-block">
|
||||
{formatImpact(node.impact)}
|
||||
{formatImpact(node.inherentImpact)}
|
||||
</div>
|
||||
</td>
|
||||
<td className="py-3 px-4">
|
||||
<div
|
||||
className={`${getRiskSeverityColor(
|
||||
node.severity
|
||||
node.inherentSeverity
|
||||
)} px-2 py-0.5 rounded-full text-xs inline-block`}
|
||||
>
|
||||
{getRiskSeverityText(node.severity)}
|
||||
{getRiskSeverityText(node.inherentSeverity)}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @generated SignedSource<<a6bd25e3b378dcc7ac26ba611a054fbe>>
|
||||
* @generated SignedSource<<016c2795072dd550f7353660e6f36afe>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
@@ -23,7 +23,11 @@ export type MesureViewRisksQuery$data = {
|
||||
readonly id: string;
|
||||
readonly inherentImpact: number;
|
||||
readonly inherentLikelihood: number;
|
||||
readonly inherentSeverity: number;
|
||||
readonly name: string;
|
||||
readonly residualImpact: number;
|
||||
readonly residualLikelihood: number;
|
||||
readonly residualSeverity: number;
|
||||
readonly updatedAt: string;
|
||||
};
|
||||
}>;
|
||||
@@ -110,6 +114,34 @@ v4 = [
|
||||
"name": "inherentImpact",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "inherentSeverity",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "residualLikelihood",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "residualImpact",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "residualSeverity",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
@@ -259,7 +291,7 @@ return {
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "b8095743d31791d2084177fde0203ed4",
|
||||
"cacheID": "d61a109844cdf35d2e6ffe92a2c9b629",
|
||||
"id": null,
|
||||
"metadata": {
|
||||
"connection": [
|
||||
@@ -276,11 +308,11 @@ return {
|
||||
},
|
||||
"name": "MesureViewRisksQuery",
|
||||
"operationKind": "query",
|
||||
"text": "query MesureViewRisksQuery(\n $mesureId: ID!\n) {\n mesure: node(id: $mesureId) {\n __typename\n id\n ... on Mesure {\n risks(first: 100) {\n edges {\n node {\n id\n name\n description\n inherentLikelihood\n inherentImpact\n createdAt\n updatedAt\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n }\n }\n}\n"
|
||||
"text": "query MesureViewRisksQuery(\n $mesureId: ID!\n) {\n mesure: node(id: $mesureId) {\n __typename\n id\n ... on Mesure {\n risks(first: 100) {\n edges {\n node {\n id\n name\n description\n inherentLikelihood\n inherentImpact\n inherentSeverity\n residualLikelihood\n residualImpact\n residualSeverity\n createdAt\n updatedAt\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n }\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "0609b3e58837ae65fb4df484b967060a";
|
||||
(node as any).hash = "9f8d865c3f979dc21ec52526f283bf4a";
|
||||
|
||||
export default node;
|
||||
|
||||
Reference in New Issue
Block a user