diff --git a/apps/console/src/pages/ControlOverviewPage.tsx b/apps/console/src/pages/ControlOverviewPage.tsx index e64d888ab..b9dc07af2 100644 --- a/apps/console/src/pages/ControlOverviewPage.tsx +++ b/apps/console/src/pages/ControlOverviewPage.tsx @@ -13,6 +13,8 @@ import { usePreloadedQuery, useQueryLoader, useMutation, + fetchQuery, + useRelayEnvironment, } from "react-relay"; import { CheckCircle2, @@ -28,6 +30,7 @@ import { File as FileGeneric, FileText, Image, + X, } from "lucide-react"; import { Card, CardContent } from "@/components/ui/card"; import { useToast } from "@/hooks/use-toast"; @@ -74,7 +77,6 @@ const controlOverviewPageQuery = graphql` edges { node { id - fileUrl mimeType filename size @@ -153,6 +155,18 @@ const uploadEvidenceMutation = graphql` } `; +// Add a GraphQL query to fetch the fileUrl for an evidence item +const getEvidenceFileUrlQuery = graphql` + query ControlOverviewPageGetEvidenceFileUrlQuery($evidenceId: ID!) { + node(id: $evidenceId) { + ... on Evidence { + id + fileUrl + } + } + } +`; + function ControlOverviewPageContent({ queryRef, }: { @@ -165,6 +179,7 @@ function ControlOverviewPageContent({ const { toast } = useToast(); const { organizationId, frameworkId, controlId } = useParams(); const navigate = useNavigate(); + const environment = useRelayEnvironment(); const [updateTaskState] = useMutation( updateTaskStateMutation @@ -207,6 +222,16 @@ function ControlOverviewPageContent({ string | null >(null); + // Add state for the preview modal + const [isPreviewModalOpen, setIsPreviewModalOpen] = useState(false); + const [previewEvidence, setPreviewEvidence] = useState<{ + id: string; + filename: string; + mimeType: string; + fileUrl?: string; + } | null>(null); + const [isLoadingFileUrl, setIsLoadingFileUrl] = useState(false); + const tasks = data.control.tasks?.edges.map((edge) => edge.node) || []; const getEvidenceConnectionId = useCallback( @@ -537,6 +562,54 @@ function ControlOverviewPageContent({ } }; + // Simplified preview handler that opens the modal and sets the evidence + const handlePreviewEvidence = (evidence: { + id: string; + filename: string; + mimeType: string; + }) => { + // Just open the modal with the evidence info + setPreviewEvidence({ + id: evidence.id, + filename: evidence.filename, + mimeType: evidence.mimeType, + }); + setIsPreviewModalOpen(true); + + // We'll fetch the fileUrl when the modal opens + setIsLoadingFileUrl(true); + + // Use GraphQL query to fetch the fileUrl + fetchQuery(environment, getEvidenceFileUrlQuery, { + evidenceId: evidence.id, + }) + .toPromise() + .then((response) => { + // Type assertion for the response + const data = response as { node?: { id: string; fileUrl?: string } }; + + if (data?.node?.fileUrl) { + setPreviewEvidence((prev) => { + if (!prev) return null; + return { ...prev, fileUrl: data.node!.fileUrl }; + }); + } else { + throw new Error("File URL not available in response"); + } + }) + .catch((error) => { + console.error("Error fetching file URL:", error); + toast({ + title: "Error fetching file URL", + description: error.message || "Could not load the file preview", + variant: "destructive", + }); + }) + .finally(() => { + setIsLoadingFileUrl(false); + }); + }; + return ( <> @@ -829,27 +902,38 @@ function ControlOverviewPageContent({
- {evidence.mimeType.startsWith("image/") && ( - + handlePreviewEvidence(evidence) + } className="p-1 rounded-full hover:bg-gray-100" - title="Preview" + title="Preview Image" > - + + ) : ( + )} - { + e.preventDefault(); + handlePreviewEvidence(evidence); + }} className="p-1 rounded-full hover:bg-gray-100" title="Download" - download > - +
); @@ -942,6 +1026,85 @@ function ControlOverviewPageContent({ + + {/* Add the Preview Modal */} + + + + + {previewEvidence?.filename} + + + +
+ {isLoadingFileUrl ? ( +
+ +

Loading preview...

+
+ ) : previewEvidence?.fileUrl ? ( + previewEvidence.mimeType.startsWith("image/") ? ( + {previewEvidence.filename} + ) : previewEvidence.mimeType.includes("pdf") ? ( +