Add requested evidence

Signed-off-by: gearnode <bryan@frimin.fr>
This commit is contained in:
gearnode
2025-04-02 10:10:49 +02:00
parent 2f9f04c984
commit c145125b1a
15 changed files with 1856 additions and 352 deletions

View File

@@ -17,30 +17,41 @@ import {
useRelayEnvironment,
} from "react-relay";
import {
AlertTriangle,
ArrowRight,
Check,
CheckCircle2,
Plus,
Trash2,
FileIcon,
Loader2,
ChevronDown,
ChevronUp,
Clock,
Download,
Eye,
File as FileGeneric,
FileIcon,
FileText,
Image,
X,
UserPlus,
UserMinus,
User,
Link2,
Loader2,
LucideIcon,
MoreHorizontal,
Plus,
Search,
Link as LinkIcon,
CheckSquare,
ShieldCheck,
AlertTriangle,
Trash2,
Upload,
User,
UserMinus,
UserPlus,
X,
} from "lucide-react";
import { Card, CardContent } from "@/components/ui/card";
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { useToast } from "@/hooks/use-toast";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
@@ -83,7 +94,6 @@ import { MitigationViewSkeleton } from "./MitigationPage";
import { MitigationViewUpdateTaskStateMutation as MitigationViewUpdateTaskStateMutationType } from "./__generated__/MitigationViewUpdateTaskStateMutation.graphql";
import { MitigationViewCreateTaskMutation as MitigationViewCreateTaskMutationType } from "./__generated__/MitigationViewCreateTaskMutation.graphql";
import { MitigationViewDeleteTaskMutation as MitigationViewDeleteTaskMutationType } from "./__generated__/MitigationViewDeleteTaskMutation.graphql";
import { MitigationViewUploadEvidenceMutation as MitigationViewUploadEvidenceMutationType } from "./__generated__/MitigationViewUploadEvidenceMutation.graphql";
import { MitigationViewDeleteEvidenceMutation as MitigationViewDeleteEvidenceMutationType } from "./__generated__/MitigationViewDeleteEvidenceMutation.graphql";
import { MitigationViewAssignTaskMutation as MitigationViewAssignTaskMutationType } from "./__generated__/MitigationViewAssignTaskMutation.graphql";
import { MitigationViewUnassignTaskMutation as MitigationViewUnassignTaskMutationType } from "./__generated__/MitigationViewUnassignTaskMutation.graphql";
@@ -105,6 +115,10 @@ import {
MitigationViewRisksQuery,
MitigationViewRisksQuery$data,
} from "./__generated__/MitigationViewRisksQuery.graphql";
import { MitigationViewCreateEvidenceMutation as MitigationViewCreateEvidenceMutationType } from "./__generated__/MitigationViewCreateEvidenceMutation.graphql";
import { MitigationViewGetEvidenceFileUrlQuery as MitigationViewGetEvidenceFileUrlQueryType } from "./__generated__/MitigationViewGetEvidenceFileUrlQuery.graphql";
import { MitigationViewFulfillEvidenceMutation as MitigationViewFulfillEvidenceMutationType } from "./__generated__/MitigationViewFulfillEvidenceMutation.graphql";
import { MitigationViewUploadEvidenceMutation as MitigationViewUploadEvidenceMutationType } from "./__generated__/MitigationViewUploadEvidenceMutation.graphql";
// Function to format ISO8601 duration to human-readable format
const formatDuration = (isoDuration: string): string => {
@@ -238,12 +252,12 @@ const deleteTaskMutation = graphql`
}
`;
const uploadEvidenceMutation = graphql`
mutation MitigationViewUploadEvidenceMutation(
$input: UploadEvidenceInput!
const createEvidenceMutation = graphql`
mutation MitigationViewCreateEvidenceMutation(
$input: CreateEvidenceInput!
$connections: [ID!]!
) {
uploadEvidence(input: $input) {
createEvidence(input: $input) {
evidenceEdge @appendEdge(connections: $connections) {
node {
id
@@ -416,6 +430,30 @@ const deleteControlMappingMutation = graphql`
}
`;
// Add a mutation to fulfill evidence
const fulfillEvidenceMutation = graphql`
mutation MitigationViewFulfillEvidenceMutation(
$input: FulfillEvidenceInput!
$connections: [ID!]!
) {
fulfillEvidence(input: $input) {
evidenceEdge @appendEdge(connections: $connections) {
node {
id
filename
fileUrl
mimeType
type
url
size
state
createdAt
}
}
}
}
`;
// Add query to fetch risks linked to a mitigation
const mitigationRisksQuery = graphql`
query MitigationViewRisksQuery($mitigationId: ID!) {
@@ -641,9 +679,9 @@ function MitigationViewContent({
useMutation<MitigationViewCreateTaskMutationType>(createTaskMutation);
const [deleteTask] =
useMutation<MitigationViewDeleteTaskMutationType>(deleteTaskMutation);
const [uploadEvidence] =
useMutation<MitigationViewUploadEvidenceMutationType>(
uploadEvidenceMutation
const [createEvidence] =
useMutation<MitigationViewCreateEvidenceMutationType>(
createEvidenceMutation
);
const [deleteEvidence] =
useMutation<MitigationViewDeleteEvidenceMutationType>(
@@ -653,6 +691,10 @@ function MitigationViewContent({
useMutation<MitigationViewAssignTaskMutationType>(assignTaskMutation);
const [unassignTask] =
useMutation<MitigationViewUnassignTaskMutationType>(unassignTaskMutation);
const [fulfillEvidence] =
useMutation<MitigationViewFulfillEvidenceMutationType>(
fulfillEvidenceMutation
);
const [updateMitigationState] =
useMutation<MitigationViewUpdateMitigationStateMutationType>(
@@ -723,6 +765,14 @@ function MitigationViewContent({
const [linkEvidenceDescription, setLinkEvidenceDescription] = useState("");
const [activeTab, setActiveTab] = useState<"file" | "link">("file");
// State for fulfill evidence dialog
const [fulfillEvidenceDialogOpen, setFulfillEvidenceDialogOpen] =
useState(false);
const [evidenceToFulfill, setEvidenceToFulfill] = useState<{
id: string;
filename: string;
} | null>(null);
// Add state for selected task panel
const [selectedTask, setSelectedTask] = useState<(typeof tasks)[0] | null>(
null
@@ -955,7 +1005,7 @@ function MitigationViewContent({
);
};
const handleUploadEvidence = (taskId: string, taskName: string) => {
const handleCreateEvidence = (taskId: string, taskName: string) => {
setTaskForEvidence({ id: taskId, name: taskName });
setEvidenceDialogOpen(true);
// Reset form fields
@@ -980,7 +1030,7 @@ function MitigationViewContent({
// Get the evidence connection ID for this task
const evidenceConnectionId = getEvidenceConnectionId(taskForEvidence.id);
uploadEvidence({
createEvidence({
variables: {
input: {
taskId: taskForEvidence.id,
@@ -1043,7 +1093,7 @@ function MitigationViewContent({
const description =
linkEvidenceDescription.trim() || `Link to ${linkEvidenceUrl}`;
uploadEvidence({
createEvidence({
variables: {
input: {
taskId: taskForEvidence.id,
@@ -1109,7 +1159,7 @@ function MitigationViewContent({
// Get the evidence connection ID for this task
const evidenceConnectionId = getEvidenceConnectionId(taskId);
uploadEvidence({
createEvidence({
variables: {
input: {
taskId: taskId,
@@ -1236,6 +1286,126 @@ function MitigationViewContent({
setIsDeleteEvidenceOpen(true);
};
const handleFulfillEvidence = (evidenceId: string, filename: string) => {
setEvidenceToFulfill({ id: evidenceId, filename });
setFulfillEvidenceDialogOpen(true);
setActiveTab("file");
setLinkEvidenceUrl("");
setLinkEvidenceDescription("");
};
const handleFulfillEvidenceWithFile = (
e: React.ChangeEvent<HTMLInputElement>
) => {
if (!e.target.files || e.target.files.length === 0 || !evidenceToFulfill)
return;
const file = e.target.files[0];
// Show toast for upload started
toast({
title: "Uploading evidence",
description: `Uploading ${file.name}...`,
variant: "default",
});
const evidenceId = evidenceToFulfill.id;
// Get connection ID for the parent task (assuming it's available in the view)
const task = tasks.find((task) =>
task.evidences?.edges.some((edge) => edge?.node?.id === evidenceId)
);
const evidenceConnectionId = task?.id
? getEvidenceConnectionId(task.id)
: null;
fulfillEvidence({
variables: {
input: {
evidenceId: evidenceId,
file: null,
},
connections: evidenceConnectionId ? [evidenceConnectionId] : [],
},
uploadables: {
"input.file": file,
},
onCompleted: () => {
setFulfillEvidenceDialogOpen(false);
setEvidenceToFulfill(null);
toast({
title: "Evidence uploaded",
description: "Evidence has been fulfilled successfully.",
variant: "default",
});
// Reset the file input
if (hiddenFileInputRef.current) {
hiddenFileInputRef.current.value = "";
}
},
onError: (error) => {
toast({
title: "Error uploading evidence",
description: error.message,
variant: "destructive",
});
},
});
};
const handleFulfillEvidenceWithLink = () => {
if (!evidenceToFulfill) return;
// Validate URL
if (!linkEvidenceUrl.trim()) {
toast({
title: "Error",
description: "Please provide a URL for the evidence",
variant: "destructive",
});
return;
}
// Get connection ID for the parent task
const task = tasks.find((task) =>
task.evidences?.edges.some(
(edge) => edge?.node?.id === evidenceToFulfill.id
)
);
const evidenceConnectionId = task?.id
? getEvidenceConnectionId(task.id)
: null;
fulfillEvidence({
variables: {
input: {
evidenceId: evidenceToFulfill.id,
url: linkEvidenceUrl,
},
connections: evidenceConnectionId ? [evidenceConnectionId] : [],
},
onCompleted: () => {
setFulfillEvidenceDialogOpen(false);
setEvidenceToFulfill(null);
setLinkEvidenceUrl("");
setLinkEvidenceDescription("");
toast({
title: "Evidence updated",
description: "Evidence has been fulfilled with link successfully.",
variant: "default",
});
},
onError: (error) => {
toast({
title: "Error updating evidence",
description: error.message,
variant: "destructive",
});
},
});
};
const confirmDeleteEvidence = () => {
if (!evidenceToDelete) return;
@@ -1789,7 +1959,7 @@ function MitigationViewContent({
>
<TabsList className="mb-4">
<TabsTrigger value="tasks" className="flex items-center gap-2">
<CheckSquare className="w-4 h-4" />
<Check className="w-4 h-4" />
Tasks
{tasks.length > 0 && (
<span className="ml-1.5 bg-blue-100 text-blue-800 rounded-full text-xs px-2 py-0.5">
@@ -1830,7 +2000,7 @@ function MitigationViewContent({
className="flex items-center gap-1"
onClick={handleOpenControlMappingDialog}
>
<LinkIcon className="w-4 h-4" />
<Link2 className="w-4 h-4" />
<span>Map to Controls</span>
</Button>
</div>
@@ -1967,7 +2137,7 @@ function MitigationViewContent({
{isLinkingControl ? (
<Loader2 className="w-4 h-4 animate-spin" />
) : (
<LinkIcon className="w-4 h-4" />
<Link2 className="w-4 h-4" />
)}
<span className="ml-1">Link</span>
</Button>
@@ -2442,7 +2612,7 @@ function MitigationViewContent({
onClick={(e) => {
e.stopPropagation();
if (task?.id && task?.name) {
handleUploadEvidence(task.id, task.name);
handleCreateEvidence(task.id, task.name);
}
}}
title="Add Evidence"
@@ -2512,16 +2682,31 @@ function MitigationViewContent({
)}
</div>
<div>
<div className="text-sm font-medium text-gray-800">
<div className="text-sm font-medium text-gray-800 flex items-center gap-2">
{evidence.filename}
{evidence.state === "REQUESTED" && (
<Badge
variant="outline"
className="text-xs bg-yellow-50 text-yellow-700 border-yellow-200"
>
Requested
</Badge>
)}
</div>
<div className="text-xs text-gray-500 flex items-center gap-2 mt-0.5">
{evidence.type === "FILE" ? (
{evidence.state === "REQUESTED" ? (
<span className="italic text-yellow-600">
Waiting for fulfillment
</span>
) : evidence.type === "FILE" ? (
<>
<span className="font-medium text-gray-600">
{formatFileSize(evidence.size)}
</span>
<span>•</span>
<span>
{formatDate(evidence.createdAt)}
</span>
</>
) : evidence.url ? (
<>
@@ -2529,16 +2714,34 @@ function MitigationViewContent({
{evidence.url}
</span>
<span>•</span>
<span>
{formatDate(evidence.createdAt)}
</span>
</>
) : null}
<span>
{formatDate(evidence.createdAt)}
</span>
) : (
<span>
{formatDate(evidence.createdAt)}
</span>
)}
</div>
</div>
</div>
<div className="flex items-center gap-1">
{evidence.type === "FILE" ? (
{evidence.state === "REQUESTED" ? (
<button
onClick={(e) => {
e.stopPropagation();
handleFulfillEvidence(
evidence.id,
evidence.filename
);
}}
className="p-1.5 rounded-full hover:bg-white hover:shadow-sm transition-all"
title="Fulfill Evidence"
>
<Upload className="w-4 h-4 text-green-600" />
</button>
) : evidence.type === "FILE" ? (
<>
{evidence.mimeType.startsWith(
"image/"
@@ -2583,13 +2786,11 @@ function MitigationViewContent({
<button
onClick={(e) => {
e.stopPropagation();
if (task.id) {
handleDeleteEvidence(
evidence.id,
evidence.filename,
task.id
);
}
handleDeleteEvidence(
evidence.id,
evidence.filename,
task.id
);
}}
className="p-1.5 rounded-full hover:bg-red-50 hover:shadow-sm transition-all"
title="Delete"
@@ -2947,7 +3148,7 @@ function MitigationViewContent({
variant="outline"
size="sm"
onClick={() =>
handleUploadEvidence(selectedTask.id, selectedTask.name)
handleCreateEvidence(selectedTask.id, selectedTask.name)
}
className="flex items-center gap-1 text-xs"
>
@@ -2974,16 +3175,31 @@ function MitigationViewContent({
{getFileIcon(evidence.mimeType, evidence.type)}
</div>
<div>
<div className="text-sm font-medium text-gray-800">
<div className="text-sm font-medium text-gray-800 flex items-center gap-2">
{evidence.filename}
{evidence.state === "REQUESTED" && (
<Badge
variant="outline"
className="text-xs bg-yellow-50 text-yellow-700 border-yellow-200"
>
Requested
</Badge>
)}
</div>
<div className="text-xs text-gray-500 flex items-center gap-2 mt-0.5">
{evidence.type === "FILE" ? (
{evidence.state === "REQUESTED" ? (
<span className="italic text-yellow-600">
Waiting for fulfillment
</span>
) : evidence.type === "FILE" ? (
<>
<span className="font-medium text-gray-600">
{formatFileSize(evidence.size)}
</span>
<span>•</span>
<span>
{formatDate(evidence.createdAt)}
</span>
</>
) : evidence.url ? (
<>
@@ -2991,20 +3207,41 @@ function MitigationViewContent({
{evidence.url}
</span>
<span>•</span>
<span>
{formatDate(evidence.createdAt)}
</span>
</>
) : null}
<span>{formatDate(evidence.createdAt)}</span>
) : (
<span>
{formatDate(evidence.createdAt)}
</span>
)}
</div>
</div>
</div>
<div className="flex items-center gap-1">
{evidence.type === "FILE" ? (
{evidence.state === "REQUESTED" ? (
<button
onClick={(e) => {
e.stopPropagation();
handleFulfillEvidence(
evidence.id,
evidence.filename
);
}}
className="p-1.5 rounded-full hover:bg-white hover:shadow-sm transition-all"
title="Fulfill Evidence"
>
<Upload className="w-4 h-4 text-green-600" />
</button>
) : evidence.type === "FILE" ? (
<>
{evidence.mimeType.startsWith("image/") ? (
<button
onClick={() =>
handlePreviewEvidence(evidence)
}
onClick={(e) => {
e.stopPropagation();
handlePreviewEvidence(evidence);
}}
className="p-1.5 rounded-full hover:bg-white hover:shadow-sm transition-all"
title="Preview Image"
>
@@ -3013,7 +3250,7 @@ function MitigationViewContent({
) : (
<button
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
handlePreviewEvidence(evidence);
}}
className="p-1.5 rounded-full hover:bg-white hover:shadow-sm transition-all"
@@ -3026,7 +3263,7 @@ function MitigationViewContent({
) : evidence.url ? (
<button
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
if (evidence.url) {
window.open(evidence.url, "_blank");
}
@@ -3039,11 +3276,11 @@ function MitigationViewContent({
) : null}
<button
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
handleDeleteEvidence(
evidence.id,
evidence.filename,
selectedTask.id
task.id
);
}}
className="p-1.5 rounded-full hover:bg-red-50 hover:shadow-sm transition-all"
@@ -3353,6 +3590,104 @@ function MitigationViewContent({
</DialogFooter>
</DialogContent>
</Dialog>
{/* Fulfill Evidence Dialog */}
<Dialog
open={fulfillEvidenceDialogOpen}
onOpenChange={setFulfillEvidenceDialogOpen}
>
<DialogContent className="sm:max-w-[525px]">
<DialogHeader>
<DialogTitle>Fulfill Evidence</DialogTitle>
<DialogDescription>
Provide the requested evidence: {evidenceToFulfill?.filename}
</DialogDescription>
</DialogHeader>
<Tabs
value={activeTab}
onValueChange={(value: string) =>
setActiveTab(value as "file" | "link")
}
>
<TabsList className="grid w-full grid-cols-2">
<TabsTrigger value="file">Document</TabsTrigger>
<TabsTrigger value="link">Link</TabsTrigger>
</TabsList>
<TabsContent value="file" className="space-y-4">
<div className="space-y-4 pt-4">
<p>Upload a document to fulfill this evidence request.</p>
<Button
onClick={() => {
if (hiddenFileInputRef.current) {
// Set a temp onchange handler for the file input
const originalOnChange =
hiddenFileInputRef.current.onchange;
hiddenFileInputRef.current.onchange = (e) => {
hiddenFileInputRef.current!.onchange =
originalOnChange as any;
handleFulfillEvidenceWithFile({
target: {
files: (e.target as HTMLInputElement)?.files,
},
} as React.ChangeEvent<HTMLInputElement>);
};
hiddenFileInputRef.current.click();
setFulfillEvidenceDialogOpen(false);
}
}}
className="w-full"
>
Select Document
</Button>
</div>
</TabsContent>
<TabsContent value="link" className="space-y-4 pt-4">
<div className="space-y-4">
<div className="grid w-full items-center gap-1.5">
<Label htmlFor="evidence-url">URL</Label>
<Input
id="evidence-url"
value={linkEvidenceUrl}
onChange={(e) => setLinkEvidenceUrl(e.target.value)}
placeholder="https://example.com"
type="url"
/>
</div>
<div className="grid w-full items-center gap-1.5">
<Label htmlFor="evidence-description">
Description (optional)
</Label>
<Textarea
id="evidence-description"
value={linkEvidenceDescription}
onChange={(e) => setLinkEvidenceDescription(e.target.value)}
placeholder="Describe this evidence (optional)"
className="min-h-[100px]"
/>
</div>
</div>
</TabsContent>
</Tabs>
<DialogFooter>
<Button
variant="outline"
onClick={() => setFulfillEvidenceDialogOpen(false)}
>
Cancel
</Button>
{activeTab === "link" && (
<Button onClick={handleFulfillEvidenceWithLink}>
Submit Link
</Button>
)}
</DialogFooter>
</DialogContent>
</Dialog>
</PageTemplate>
);
}

View File

@@ -0,0 +1,229 @@
/**
* @generated SignedSource<<beb765793d4559382ee625dffd7a510c>>
* @lightSyntaxTransform
* @nogrep
*/
/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
import { ConcreteRequest } from 'relay-runtime';
export type EvidenceState = "FULFILLED" | "REQUESTED";
export type EvidenceType = "FILE" | "LINK";
export type CreateEvidenceInput = {
description: string;
file?: any | null | undefined;
name: string;
taskId: string;
type: EvidenceType;
url?: string | null | undefined;
};
export type MitigationViewCreateEvidenceMutation$variables = {
connections: ReadonlyArray<string>;
input: CreateEvidenceInput;
};
export type MitigationViewCreateEvidenceMutation$data = {
readonly createEvidence: {
readonly evidenceEdge: {
readonly node: {
readonly createdAt: string;
readonly fileUrl: string | null | undefined;
readonly filename: string;
readonly id: string;
readonly mimeType: string;
readonly size: number;
readonly state: EvidenceState;
readonly type: EvidenceType;
readonly url: string | null | undefined;
};
};
};
};
export type MitigationViewCreateEvidenceMutation = {
response: MitigationViewCreateEvidenceMutation$data;
variables: MitigationViewCreateEvidenceMutation$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,
"concreteType": "EvidenceEdge",
"kind": "LinkedField",
"name": "evidenceEdge",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "Evidence",
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "id",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "filename",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "fileUrl",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "mimeType",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "type",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "url",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "size",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "state",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "createdAt",
"storageKey": null
}
],
"storageKey": null
}
],
"storageKey": null
};
return {
"fragment": {
"argumentDefinitions": [
(v0/*: any*/),
(v1/*: any*/)
],
"kind": "Fragment",
"metadata": null,
"name": "MitigationViewCreateEvidenceMutation",
"selections": [
{
"alias": null,
"args": (v2/*: any*/),
"concreteType": "CreateEvidencePayload",
"kind": "LinkedField",
"name": "createEvidence",
"plural": false,
"selections": [
(v3/*: any*/)
],
"storageKey": null
}
],
"type": "Mutation",
"abstractKey": null
},
"kind": "Request",
"operation": {
"argumentDefinitions": [
(v1/*: any*/),
(v0/*: any*/)
],
"kind": "Operation",
"name": "MitigationViewCreateEvidenceMutation",
"selections": [
{
"alias": null,
"args": (v2/*: any*/),
"concreteType": "CreateEvidencePayload",
"kind": "LinkedField",
"name": "createEvidence",
"plural": false,
"selections": [
(v3/*: any*/),
{
"alias": null,
"args": null,
"filters": null,
"handle": "appendEdge",
"key": "",
"kind": "LinkedHandle",
"name": "evidenceEdge",
"handleArgs": [
{
"kind": "Variable",
"name": "connections",
"variableName": "connections"
}
]
}
],
"storageKey": null
}
]
},
"params": {
"cacheID": "ca3148cb5b3c59fec3bd9ff9c281f364",
"id": null,
"metadata": {},
"name": "MitigationViewCreateEvidenceMutation",
"operationKind": "mutation",
"text": "mutation MitigationViewCreateEvidenceMutation(\n $input: CreateEvidenceInput!\n) {\n createEvidence(input: $input) {\n evidenceEdge {\n node {\n id\n filename\n fileUrl\n mimeType\n type\n url\n size\n state\n createdAt\n }\n }\n }\n}\n"
}
};
})();
(node as any).hash = "dac05ab414b2e09e801df5b0be9532a3";
export default node;

View File

@@ -0,0 +1,226 @@
/**
* @generated SignedSource<<c12714c0dab6d31865a2b49936566f05>>
* @lightSyntaxTransform
* @nogrep
*/
/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
import { ConcreteRequest } from 'relay-runtime';
export type EvidenceState = "FULFILLED" | "REQUESTED";
export type EvidenceType = "FILE" | "LINK";
export type FulfillEvidenceInput = {
evidenceId: string;
file?: any | null | undefined;
url?: string | null | undefined;
};
export type MitigationViewFulfillEvidenceMutation$variables = {
connections: ReadonlyArray<string>;
input: FulfillEvidenceInput;
};
export type MitigationViewFulfillEvidenceMutation$data = {
readonly fulfillEvidence: {
readonly evidenceEdge: {
readonly node: {
readonly createdAt: string;
readonly fileUrl: string | null | undefined;
readonly filename: string;
readonly id: string;
readonly mimeType: string;
readonly size: number;
readonly state: EvidenceState;
readonly type: EvidenceType;
readonly url: string | null | undefined;
};
};
};
};
export type MitigationViewFulfillEvidenceMutation = {
response: MitigationViewFulfillEvidenceMutation$data;
variables: MitigationViewFulfillEvidenceMutation$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,
"concreteType": "EvidenceEdge",
"kind": "LinkedField",
"name": "evidenceEdge",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "Evidence",
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "id",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "filename",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "fileUrl",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "mimeType",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "type",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "url",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "size",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "state",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "createdAt",
"storageKey": null
}
],
"storageKey": null
}
],
"storageKey": null
};
return {
"fragment": {
"argumentDefinitions": [
(v0/*: any*/),
(v1/*: any*/)
],
"kind": "Fragment",
"metadata": null,
"name": "MitigationViewFulfillEvidenceMutation",
"selections": [
{
"alias": null,
"args": (v2/*: any*/),
"concreteType": "FulfillEvidencePayload",
"kind": "LinkedField",
"name": "fulfillEvidence",
"plural": false,
"selections": [
(v3/*: any*/)
],
"storageKey": null
}
],
"type": "Mutation",
"abstractKey": null
},
"kind": "Request",
"operation": {
"argumentDefinitions": [
(v1/*: any*/),
(v0/*: any*/)
],
"kind": "Operation",
"name": "MitigationViewFulfillEvidenceMutation",
"selections": [
{
"alias": null,
"args": (v2/*: any*/),
"concreteType": "FulfillEvidencePayload",
"kind": "LinkedField",
"name": "fulfillEvidence",
"plural": false,
"selections": [
(v3/*: any*/),
{
"alias": null,
"args": null,
"filters": null,
"handle": "appendEdge",
"key": "",
"kind": "LinkedHandle",
"name": "evidenceEdge",
"handleArgs": [
{
"kind": "Variable",
"name": "connections",
"variableName": "connections"
}
]
}
],
"storageKey": null
}
]
},
"params": {
"cacheID": "147fdc881f797ace12dc1242cab782ad",
"id": null,
"metadata": {},
"name": "MitigationViewFulfillEvidenceMutation",
"operationKind": "mutation",
"text": "mutation MitigationViewFulfillEvidenceMutation(\n $input: FulfillEvidenceInput!\n) {\n fulfillEvidence(input: $input) {\n evidenceEdge {\n node {\n id\n filename\n fileUrl\n mimeType\n type\n url\n size\n state\n createdAt\n }\n }\n }\n}\n"
}
};
})();
(node as any).hash = "c0a1e6be43759cfbcd7e25a3b597926a";
export default node;

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<5baf9229b2f520bcfa43960bd6af05e0>>
* @generated SignedSource<<3188657dfa050c818bf0dc06688d7fab>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -9,7 +9,7 @@
// @ts-nocheck
import { ConcreteRequest } from 'relay-runtime';
export type EvidenceState = "EXPIRED" | "INVALID" | "VALID";
export type EvidenceState = "FULFILLED" | "REQUESTED";
export type EvidenceType = "FILE" | "LINK";
export type MitigationImportance = "ADVANCED" | "MANDATORY" | "PREFERRED";
export type MitigationState = "IMPLEMENTED" | "IN_PROGRESS" | "NOT_APPLICABLE" | "NOT_STARTED";

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<9d4db1a6dc5ce7a35be41b6f42bc91d2>>
* @generated SignedSource<<6e5efc598a8e6657fd0a1375e6104ff6>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -9,7 +9,7 @@
// @ts-nocheck
import { ConcreteRequest } from 'relay-runtime';
export type EvidenceState = "EXPIRED" | "INVALID" | "VALID";
export type EvidenceState = "FULLFILLED" | "REQUESTED";
export type EvidenceType = "FILE" | "LINK";
export type UploadEvidenceInput = {
description: string;