@@ -87,10 +87,14 @@ import { MitigationViewUnassignTaskMutation as MitigationViewUnassignTaskMutatio
|
|||||||
import { MitigationViewUpdateMitigationStateMutation as MitigationViewUpdateMitigationStateMutationType } from "./__generated__/MitigationViewUpdateMitigationStateMutation.graphql";
|
import { MitigationViewUpdateMitigationStateMutation as MitigationViewUpdateMitigationStateMutationType } from "./__generated__/MitigationViewUpdateMitigationStateMutation.graphql";
|
||||||
import { MitigationViewQuery as MitigationViewQueryType } from "./__generated__/MitigationViewQuery.graphql";
|
import { MitigationViewQuery as MitigationViewQueryType } from "./__generated__/MitigationViewQuery.graphql";
|
||||||
import { MitigationViewOrganizationQuery$data } from "./__generated__/MitigationViewOrganizationQuery.graphql";
|
import { MitigationViewOrganizationQuery$data } from "./__generated__/MitigationViewOrganizationQuery.graphql";
|
||||||
import { MitigationViewFrameworksQuery$data } from "./__generated__/MitigationViewFrameworksQuery.graphql";
|
import {
|
||||||
import { MitigationViewLinkedControlsQuery$data } from "./__generated__/MitigationViewLinkedControlsQuery.graphql";
|
MitigationViewFrameworksQuery,
|
||||||
import { MitigationViewCreateControlMappingMutation$data } from "./__generated__/MitigationViewCreateControlMappingMutation.graphql";
|
MitigationViewFrameworksQuery$data,
|
||||||
import { MitigationViewDeleteControlMappingMutation$data } from "./__generated__/MitigationViewDeleteControlMappingMutation.graphql";
|
} from "./__generated__/MitigationViewFrameworksQuery.graphql";
|
||||||
|
import {
|
||||||
|
MitigationViewLinkedControlsQuery,
|
||||||
|
MitigationViewLinkedControlsQuery$data,
|
||||||
|
} from "./__generated__/MitigationViewLinkedControlsQuery.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 => {
|
||||||
@@ -402,6 +406,31 @@ const deleteControlMappingMutation = 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;
|
||||||
|
};
|
||||||
|
|
||||||
function MitigationViewContent({
|
function MitigationViewContent({
|
||||||
queryRef,
|
queryRef,
|
||||||
}: {
|
}: {
|
||||||
@@ -427,10 +456,10 @@ function MitigationViewContent({
|
|||||||
// Control mapping state
|
// Control mapping state
|
||||||
const [isControlMappingDialogOpen, setIsControlMappingDialogOpen] =
|
const [isControlMappingDialogOpen, setIsControlMappingDialogOpen] =
|
||||||
useState(false);
|
useState(false);
|
||||||
const [frameworksData, setFrameworksData] = useState<any | null>(null);
|
const [frameworksData, setFrameworksData] =
|
||||||
const [linkedControlsData, setLinkedControlsData] = useState<any | null>(
|
useState<MitigationViewFrameworksQuery$data | null>(null);
|
||||||
null
|
const [linkedControlsData, setLinkedControlsData] =
|
||||||
);
|
useState<MitigationViewLinkedControlsQuery$data | null>(null);
|
||||||
const [controlSearchQuery, setControlSearchQuery] = useState("");
|
const [controlSearchQuery, setControlSearchQuery] = useState("");
|
||||||
const [selectedFrameworkId, setSelectedFrameworkId] = useState<string | null>(
|
const [selectedFrameworkId, setSelectedFrameworkId] = useState<string | null>(
|
||||||
null
|
null
|
||||||
@@ -450,10 +479,10 @@ function MitigationViewContent({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (organizationId) {
|
if (organizationId) {
|
||||||
fetchQuery(environment, organizationQuery, { organizationId }).subscribe({
|
fetchQuery(environment, organizationQuery, { organizationId }).subscribe({
|
||||||
next: (data) => {
|
next: (data: unknown) => {
|
||||||
setOrganizationData(data);
|
setOrganizationData(data as MitigationViewOrganizationQuery$data);
|
||||||
},
|
},
|
||||||
error: (error) => {
|
error: (error: Error) => {
|
||||||
console.error("Error fetching organization:", error);
|
console.error("Error fetching organization:", error);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -464,10 +493,10 @@ function MitigationViewContent({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (mitigationId) {
|
if (mitigationId) {
|
||||||
fetchQuery(environment, linkedControlsQuery, { mitigationId }).subscribe({
|
fetchQuery(environment, linkedControlsQuery, { mitigationId }).subscribe({
|
||||||
next: (data) => {
|
next: (data: unknown) => {
|
||||||
setLinkedControlsData(data);
|
setLinkedControlsData(data as MitigationViewLinkedControlsQuery$data);
|
||||||
},
|
},
|
||||||
error: (error) => {
|
error: (error: Error) => {
|
||||||
console.error("Error fetching linked controls:", error);
|
console.error("Error fetching linked controls:", error);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -1356,15 +1385,21 @@ function MitigationViewContent({
|
|||||||
setIsLoadingControls(true);
|
setIsLoadingControls(true);
|
||||||
|
|
||||||
// Fetch all frameworks and their controls
|
// Fetch all frameworks and their controls
|
||||||
fetchQuery(environment, frameworksQuery, { organizationId }).subscribe({
|
fetchQuery<MitigationViewFrameworksQuery>(environment, frameworksQuery, {
|
||||||
next: (data: any) => {
|
organizationId,
|
||||||
|
}).subscribe({
|
||||||
|
next: (data) => {
|
||||||
setFrameworksData(data);
|
setFrameworksData(data);
|
||||||
if (
|
if (
|
||||||
data?.organization?.frameworks?.edges?.length > 0 &&
|
data?.organization?.frameworks?.edges &&
|
||||||
|
data.organization.frameworks.edges.length > 0 &&
|
||||||
!selectedFrameworkId
|
!selectedFrameworkId
|
||||||
) {
|
) {
|
||||||
// Select the first framework by default if none is selected
|
// Select the first framework by default if none is selected
|
||||||
setSelectedFrameworkId(data.organization.frameworks.edges[0].node.id);
|
const frameworks = data.organization.frameworks.edges;
|
||||||
|
if (frameworks[0]?.node?.id) {
|
||||||
|
setSelectedFrameworkId(frameworks[0].node.id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
complete: () => {
|
complete: () => {
|
||||||
@@ -1372,11 +1407,13 @@ function MitigationViewContent({
|
|||||||
fetchQuery(environment, linkedControlsQuery, {
|
fetchQuery(environment, linkedControlsQuery, {
|
||||||
mitigationId,
|
mitigationId,
|
||||||
}).subscribe({
|
}).subscribe({
|
||||||
next: (data: any) => {
|
next: (data: unknown) => {
|
||||||
setLinkedControlsData(data);
|
setLinkedControlsData(
|
||||||
|
data as MitigationViewLinkedControlsQuery$data
|
||||||
|
);
|
||||||
setIsLoadingControls(false);
|
setIsLoadingControls(false);
|
||||||
},
|
},
|
||||||
error: (error) => {
|
error: (error: Error) => {
|
||||||
console.error("Error fetching linked controls:", error);
|
console.error("Error fetching linked controls:", error);
|
||||||
setIsLoadingControls(false);
|
setIsLoadingControls(false);
|
||||||
toast({
|
toast({
|
||||||
@@ -1387,7 +1424,7 @@ function MitigationViewContent({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
error: (error) => {
|
error: (error: Error) => {
|
||||||
console.error("Error fetching frameworks:", error);
|
console.error("Error fetching frameworks:", error);
|
||||||
setIsLoadingControls(false);
|
setIsLoadingControls(false);
|
||||||
toast({
|
toast({
|
||||||
@@ -1403,28 +1440,27 @@ function MitigationViewContent({
|
|||||||
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.edges;
|
const frameworks = frameworksData.organization.frameworks
|
||||||
|
.edges as Array<FrameworkEdge>;
|
||||||
if (selectedFrameworkId) {
|
if (selectedFrameworkId) {
|
||||||
const selectedFramework = frameworks.find(
|
const selectedFramework = frameworks.find(
|
||||||
(edge: any) => edge.node.id === selectedFrameworkId
|
(edge: FrameworkEdge) => edge.node.id === selectedFrameworkId
|
||||||
);
|
);
|
||||||
|
|
||||||
if (selectedFramework?.node?.controls?.edges) {
|
if (selectedFramework?.node?.controls?.edges) {
|
||||||
return selectedFramework.node.controls.edges.map(
|
return selectedFramework.node.controls.edges.map((edge) => edge.node);
|
||||||
(edge: any) => edge.node
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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: any) =>
|
return frameworks.flatMap((framework: FrameworkEdge) =>
|
||||||
framework.node.controls.edges.map((edge: any) => edge.node)
|
framework.node.controls.edges.map((edge) => edge.node)
|
||||||
);
|
);
|
||||||
}, [frameworksData, selectedFrameworkId]);
|
}, [frameworksData, selectedFrameworkId]);
|
||||||
|
|
||||||
const getLinkedControls = useCallback(() => {
|
const getLinkedControls = useCallback(() => {
|
||||||
if (!linkedControlsData?.mitigation?.controls?.edges) return [];
|
if (!linkedControlsData?.mitigation?.controls?.edges) return [];
|
||||||
return linkedControlsData.mitigation.controls.edges.map(
|
return (linkedControlsData.mitigation.controls.edges || []).map(
|
||||||
(edge) => edge.node
|
(edge) => edge.node
|
||||||
);
|
);
|
||||||
}, [linkedControlsData]);
|
}, [linkedControlsData]);
|
||||||
@@ -1432,7 +1468,9 @@ function MitigationViewContent({
|
|||||||
const isControlLinked = useCallback(
|
const isControlLinked = useCallback(
|
||||||
(controlId: string) => {
|
(controlId: string) => {
|
||||||
const linkedControls = getLinkedControls();
|
const linkedControls = getLinkedControls();
|
||||||
return linkedControls.some((control: any) => control.id === controlId);
|
return linkedControls.some(
|
||||||
|
(control: ControlNode) => control.id === controlId
|
||||||
|
);
|
||||||
},
|
},
|
||||||
[getLinkedControls]
|
[getLinkedControls]
|
||||||
);
|
);
|
||||||
@@ -1464,13 +1502,17 @@ function MitigationViewContent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Refresh linked controls data
|
// Refresh linked controls data
|
||||||
fetchQuery(environment, linkedControlsQuery, {
|
fetchQuery<MitigationViewLinkedControlsQuery>(
|
||||||
|
environment,
|
||||||
|
linkedControlsQuery,
|
||||||
|
{
|
||||||
mitigationId,
|
mitigationId,
|
||||||
}).subscribe({
|
}
|
||||||
next: (data: any) => {
|
).subscribe({
|
||||||
|
next: (data) => {
|
||||||
setLinkedControlsData(data);
|
setLinkedControlsData(data);
|
||||||
},
|
},
|
||||||
error: (error) => {
|
error: (error: Error) => {
|
||||||
console.error("Error refreshing linked controls:", error);
|
console.error("Error refreshing linked controls:", error);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -1524,10 +1566,12 @@ function MitigationViewContent({
|
|||||||
fetchQuery(environment, linkedControlsQuery, {
|
fetchQuery(environment, linkedControlsQuery, {
|
||||||
mitigationId,
|
mitigationId,
|
||||||
}).subscribe({
|
}).subscribe({
|
||||||
next: (data: any) => {
|
next: (data: unknown) => {
|
||||||
setLinkedControlsData(data);
|
setLinkedControlsData(
|
||||||
|
data as MitigationViewLinkedControlsQuery$data
|
||||||
|
);
|
||||||
},
|
},
|
||||||
error: (error) => {
|
error: (error: Error) => {
|
||||||
console.error("Error refreshing linked controls:", error);
|
console.error("Error refreshing linked controls:", error);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -1562,7 +1606,7 @@ function MitigationViewContent({
|
|||||||
|
|
||||||
const lowerQuery = controlSearchQuery.toLowerCase();
|
const lowerQuery = controlSearchQuery.toLowerCase();
|
||||||
return controls.filter(
|
return controls.filter(
|
||||||
(control: any) =>
|
(control: ControlNode) =>
|
||||||
control.referenceId.toLowerCase().includes(lowerQuery) ||
|
control.referenceId.toLowerCase().includes(lowerQuery) ||
|
||||||
control.name.toLowerCase().includes(lowerQuery) ||
|
control.name.toLowerCase().includes(lowerQuery) ||
|
||||||
(control.description &&
|
(control.description &&
|
||||||
@@ -1673,7 +1717,7 @@ function MitigationViewContent({
|
|||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectItem value="all">All Frameworks</SelectItem>
|
<SelectItem value="all">All Frameworks</SelectItem>
|
||||||
{frameworksData?.organization?.frameworks?.edges?.map(
|
{frameworksData?.organization?.frameworks?.edges?.map(
|
||||||
(edge: any) => (
|
(edge) => (
|
||||||
<SelectItem key={edge.node.id} value={edge.node.id}>
|
<SelectItem key={edge.node.id} value={edge.node.id}>
|
||||||
{edge.node.name}
|
{edge.node.name}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
@@ -1698,7 +1742,7 @@ function MitigationViewContent({
|
|||||||
different framework.
|
different framework.
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
filteredControls().map((control: any) => {
|
filteredControls().map((control) => {
|
||||||
const isLinked = isControlLinked(control.id);
|
const isLinked = isControlLinked(control.id);
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
@@ -1788,9 +1832,10 @@ function MitigationViewContent({
|
|||||||
{/* Linked Controls List */}
|
{/* Linked Controls List */}
|
||||||
<Card>
|
<Card>
|
||||||
<CardContent className="p-4">
|
<CardContent className="p-4">
|
||||||
{linkedControlsData?.mitigation?.controls?.edges?.length > 0 ? (
|
{linkedControlsData?.mitigation?.controls?.edges &&
|
||||||
|
linkedControlsData.mitigation.controls.edges.length > 0 ? (
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
{getLinkedControls().map((control: any) => (
|
{getLinkedControls().map((control: ControlNode) => (
|
||||||
<Card key={control.id} className="border overflow-hidden">
|
<Card key={control.id} className="border overflow-hidden">
|
||||||
<div className="p-3">
|
<div className="p-3">
|
||||||
<div className="flex items-center gap-2 mb-1">
|
<div className="flex items-center gap-2 mb-1">
|
||||||
@@ -1821,8 +1866,8 @@ function MitigationViewContent({
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="text-center py-8 text-gray-500">
|
<div className="text-center py-8 text-gray-500">
|
||||||
No controls linked to this mitigation yet. Click "Map to
|
No controls linked to this mitigation yet. Click "Map to
|
||||||
Controls" to link controls.
|
Controls" to link controls.
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
|||||||
Reference in New Issue
Block a user