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