Add risk list and create risk

Signed-off-by: gearnode <bryan@frimin.fr>
This commit is contained in:
gearnode
2025-03-30 16:51:24 +02:00
parent 6210458fd9
commit 5e601a0ef0
21 changed files with 4384 additions and 8 deletions

View File

@@ -22,6 +22,8 @@ import { UpdatePolicyPage } from "./policies/UpdatePolicyPage";
import { VendorListPage } from "./vendors/VendorListPage";
import { VendorPage } from "./vendors/VendorPage";
import { MitigationNewPage } from "./mitigations/MitigationNewPage";
import { RiskListPage } from "./risks/RiskListPage";
import { NewRiskPage } from "./risks/NewRiskPage";
export function OrganizationsRoutes() {
return (
@@ -53,6 +55,8 @@ export function OrganizationsRoutes() {
path="policies/:policyId/update"
element={<UpdatePolicyPage />}
/>
<Route path="risks" element={<RiskListPage />} />
<Route path="risks/new" element={<NewRiskPage />} />
<Route path="settings" element={<SettingsPage />} />
<Route path="*" element={<NotFoundPage />} />
</Route>

View File

@@ -0,0 +1,29 @@
import { PageTemplateSkeleton } from "@/components/PageTemplate";
import { Suspense } from "react";
import { lazy } from "@probo/react-lazy";
import { useLocation } from "react-router";
import { ErrorBoundaryWithLocation } from "../ErrorBoundary";
const NewRiskView = lazy(() => import("./NewRiskView"));
export function NewRiskViewSkeleton() {
return (
<PageTemplateSkeleton>
<div className="space-y-2">
<div className="h-96 bg-muted animate-pulse rounded-lg" />
</div>
</PageTemplateSkeleton>
);
}
export function NewRiskPage() {
const location = useLocation();
return (
<Suspense key={location.pathname} fallback={<NewRiskViewSkeleton />}>
<ErrorBoundaryWithLocation>
<NewRiskView />
</ErrorBoundaryWithLocation>
</Suspense>
);
}

View File

@@ -0,0 +1,163 @@
import { useState } from "react";
import { useNavigate, useParams } from "react-router";
import { ConnectionHandler, graphql, useMutation } from "react-relay";
import {
Card,
CardContent,
CardHeader,
CardTitle,
CardDescription,
} from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
import { useToast } from "@/hooks/use-toast";
import { PageTemplate } from "@/components/PageTemplate";
const createRiskMutation = graphql`
mutation NewRiskViewCreateRiskMutation(
$input: CreateRiskInput!
$connections: [ID!]!
) {
createRisk(input: $input) {
riskEdge @prependEdge(connections: $connections) {
node {
id
name
description
createdAt
updatedAt
}
}
}
}
`;
export default function NewRiskView() {
const navigate = useNavigate();
const { organizationId } = useParams<{ organizationId: string }>();
const [name, setName] = useState("");
const [description, setDescription] = useState("");
const [isSubmitting, setIsSubmitting] = useState(false);
const { toast } = useToast();
const [commitMutation, isInFlight] = useMutation(createRiskMutation);
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (!name.trim()) {
toast({
title: "Error",
description: "Please enter a name for the risk.",
variant: "destructive",
});
return;
}
setIsSubmitting(true);
const input = {
organizationId: organizationId!,
name,
description,
};
commitMutation({
variables: {
input,
connections: [
ConnectionHandler.getConnectionID(
organizationId!,
"RiskListView_risks"
),
],
},
onCompleted: (response, errors) => {
setIsSubmitting(false);
if (errors) {
console.error("Error creating risk:", errors);
toast({
title: "Error",
description: "Failed to create risk. Please try again.",
variant: "destructive",
});
return;
}
toast({
title: "Success",
description: "Risk created successfully!",
});
navigate(`/organizations/${organizationId}/risks`);
},
onError: (error) => {
setIsSubmitting(false);
console.error("Error creating risk:", error);
toast({
title: "Error",
description: "Failed to create risk. Please try again.",
variant: "destructive",
});
},
});
};
return (
<PageTemplate
title="Create Risk"
description="Add a new risk to your organization"
>
<Card className="max-w-2xl">
<CardHeader>
<CardTitle>Risk Details</CardTitle>
<CardDescription>
Enter the details of the risk you want to add to your organization.
</CardDescription>
</CardHeader>
<CardContent>
<form onSubmit={handleSubmit} className="space-y-6">
<div className="space-y-2">
<Label htmlFor="name">Name</Label>
<Input
id="name"
placeholder="Risk name"
value={name}
onChange={(e) => setName(e.target.value)}
required
/>
</div>
<div className="space-y-2">
<Label htmlFor="description">Description</Label>
<Textarea
id="description"
placeholder="Describe the risk in detail"
value={description}
onChange={(e) => setDescription(e.target.value)}
rows={5}
/>
</div>
<div className="flex justify-end gap-3">
<Button
type="button"
variant="outline"
onClick={() =>
navigate(`/organizations/${organizationId}/risks`)
}
>
Cancel
</Button>
<Button type="submit" disabled={isInFlight || isSubmitting}>
{isInFlight || isSubmitting ? "Creating..." : "Create Risk"}
</Button>
</div>
</form>
</CardContent>
</Card>
</PageTemplate>
);
}

View File

@@ -0,0 +1,31 @@
import { PageTemplateSkeleton } from "@/components/PageTemplate";
import { Suspense } from "react";
import { lazy } from "@probo/react-lazy";
import { useLocation } from "react-router";
import { ErrorBoundaryWithLocation } from "../ErrorBoundary";
const RiskListView = lazy(() => import("./RiskListView"));
export function RiskViewSkeleton() {
return (
<PageTemplateSkeleton>
<div className="space-y-2">
{[1, 2].map((i) => (
<div key={i} className="h-20 bg-muted animate-pulse rounded-lg" />
))}
</div>
</PageTemplateSkeleton>
);
}
export function RiskListPage() {
const location = useLocation();
return (
<Suspense key={location.pathname} fallback={<RiskViewSkeleton />}>
<ErrorBoundaryWithLocation>
<RiskListView />
</ErrorBoundaryWithLocation>
</Suspense>
);
}

View File

@@ -0,0 +1,404 @@
"use client";
import {
graphql,
PreloadedQuery,
usePaginationFragment,
usePreloadedQuery,
useQueryLoader,
useMutation,
} from "react-relay";
import {
Suspense,
useCallback,
useEffect,
useState,
useTransition,
} from "react";
import type { RiskListViewQuery } from "./__generated__/RiskListViewQuery.graphql";
import { useParams, useSearchParams } from "react-router";
import { PageTemplate } from "@/components/PageTemplate";
import { RiskViewSkeleton } from "./RiskListPage";
import { RiskListViewPaginationQuery } from "./__generated__/RiskListViewPaginationQuery.graphql";
import { RiskListView_risks$key } from "./__generated__/RiskListView_risks.graphql";
import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
import { Link } from "react-router";
import { Plus, Trash2 } from "lucide-react";
import { useToast } from "@/hooks/use-toast";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { RiskListViewDeleteMutation } from "./__generated__/RiskListViewDeleteMutation.graphql";
const defaultPageSize = 25;
const riskListViewQuery = graphql`
query RiskListViewQuery(
$organizationId: ID!
$first: Int
$after: CursorKey
$last: Int
$before: CursorKey
) {
organization: node(id: $organizationId) {
id
...RiskListView_risks
@arguments(first: $first, after: $after, last: $last, before: $before)
}
}
`;
const riskListFragment = graphql`
fragment RiskListView_risks on Organization
@refetchable(queryName: "RiskListViewPaginationQuery")
@argumentDefinitions(
first: { type: "Int" }
after: { type: "CursorKey" }
last: { type: "Int" }
before: { type: "CursorKey" }
) {
risks(first: $first, after: $after, last: $last, before: $before)
@connection(key: "RiskListView_risks") {
__id
edges {
node {
id
name
description
createdAt
updatedAt
}
}
pageInfo {
hasNextPage
hasPreviousPage
startCursor
endCursor
}
}
}
`;
const deleteRiskMutation = graphql`
mutation RiskListViewDeleteMutation(
$input: DeleteRiskInput!
$connections: [ID!]!
) {
deleteRisk(input: $input) {
deletedRiskId @deleteEdge(connections: $connections)
}
}
`;
function LoadAboveButton({
isLoading,
hasMore,
onLoadMore,
}: {
isLoading: boolean;
hasMore: boolean;
onLoadMore: () => void;
}) {
if (!hasMore) {
return null;
}
return (
<div className="flex justify-center mb-6">
<Button
variant="outline"
onClick={onLoadMore}
disabled={isLoading}
className="w-full"
>
{isLoading ? "Loading..." : "Load above"}
</Button>
</div>
);
}
function LoadBelowButton({
isLoading,
hasMore,
onLoadMore,
}: {
isLoading: boolean;
hasMore: boolean;
onLoadMore: () => void;
}) {
if (!hasMore) {
return null;
}
return (
<div className="flex justify-center mt-6">
<Button
variant="outline"
onClick={onLoadMore}
disabled={isLoading || !hasMore}
className="w-full"
>
{isLoading ? "Loading..." : "Load below"}
</Button>
</div>
);
}
function RiskListViewContent({
queryRef,
}: {
queryRef: PreloadedQuery<RiskListViewQuery>;
}) {
const data = usePreloadedQuery(riskListViewQuery, queryRef);
const [, setSearchParams] = useSearchParams();
const [, startTransition] = useTransition();
const { organizationId } = useParams<{ organizationId: string }>();
const { toast } = useToast();
// State for delete confirmation dialog
const [riskToDelete, setRiskToDelete] = useState<{
id: string;
name: string;
} | null>(null);
const [isDeleting, setIsDeleting] = useState(false);
// Setup delete mutation
const [commitDeleteMutation] =
useMutation<RiskListViewDeleteMutation>(deleteRiskMutation);
const {
data: risksConnection,
loadNext,
loadPrevious,
hasNext,
hasPrevious,
isLoadingNext,
isLoadingPrevious,
} = usePaginationFragment<
RiskListViewPaginationQuery,
RiskListView_risks$key
>(riskListFragment, data.organization);
const risks = risksConnection?.risks?.edges?.map((edge) => edge.node) || [];
const pageInfo = risksConnection?.risks?.pageInfo;
const connectionId = risksConnection?.risks?.__id;
// Handle delete risk
const handleDeleteRisk = useCallback(() => {
if (!riskToDelete || !connectionId) return;
setIsDeleting(true);
commitDeleteMutation({
variables: {
input: {
riskId: riskToDelete.id,
},
connections: [connectionId],
},
onCompleted: (_, errors) => {
setIsDeleting(false);
setRiskToDelete(null);
if (errors) {
console.error("Error deleting risk:", errors);
toast({
title: "Error",
description: "Failed to delete risk. Please try again.",
variant: "destructive",
});
return;
}
toast({
title: "Success",
description: "Risk deleted successfully.",
});
},
onError: (error) => {
setIsDeleting(false);
setRiskToDelete(null);
console.error("Error deleting risk:", error);
toast({
title: "Error",
description: "Failed to delete risk. Please try again.",
variant: "destructive",
});
},
});
}, [riskToDelete, connectionId, commitDeleteMutation, toast]);
return (
<PageTemplate
title="Risks"
actions={
<Button asChild>
<Link to={`/organizations/${organizationId}/risks/new`}>
<Plus className="mr-2 h-4 w-4" />
New Risk
</Link>
</Button>
}
>
<div className="space-y-6">
<LoadAboveButton
isLoading={isLoadingPrevious}
hasMore={hasPrevious}
onLoadMore={() => {
startTransition(() => {
setSearchParams((prev) => {
prev.set("before", pageInfo?.startCursor || "");
prev.delete("after");
return prev;
});
loadPrevious(defaultPageSize);
});
}}
/>
<Card>
<CardContent className="p-0">
<div className="w-full overflow-auto">
<table className="w-full caption-bottom text-sm">
<thead className="[&_tr]:border-b">
<tr className="border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted">
<th className="h-12 px-4 text-left align-middle font-medium text-muted-foreground">
Name
</th>
<th className="h-12 px-4 text-left align-middle font-medium text-muted-foreground">
Description
</th>
<th className="h-12 px-4 text-left align-middle font-medium text-muted-foreground">
Actions
</th>
</tr>
</thead>
<tbody className="[&_tr:last-child]:border-0">
{risks.length === 0 ? (
<tr className="border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted">
<td
colSpan={3}
className="text-center p-4 align-middle text-muted-foreground"
>
No risks found. Create a new risk to get started.
</td>
</tr>
) : (
risks.map((risk) => (
<tr
key={risk.id}
className="border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted cursor-pointer"
>
<td className="p-4 align-middle font-medium">
{risk.name}
</td>
<td className="p-4 align-middle">{risk.description}</td>
<td className="p-4 align-middle">
<Button
variant="ghost"
size="icon"
onClick={(e) => {
e.stopPropagation();
setRiskToDelete({ id: risk.id, name: risk.name });
}}
>
<Trash2 className="h-4 w-4 text-destructive" />
</Button>
</td>
</tr>
))
)}
</tbody>
</table>
</div>
</CardContent>
</Card>
<LoadBelowButton
isLoading={isLoadingNext}
hasMore={hasNext}
onLoadMore={() => {
startTransition(() => {
setSearchParams((prev) => {
prev.set("after", pageInfo?.endCursor || "");
prev.delete("before");
return prev;
});
loadNext(defaultPageSize);
});
}}
/>
{/* Delete Confirmation Dialog */}
<Dialog
open={!!riskToDelete}
onOpenChange={(open) => !open && setRiskToDelete(null)}
>
<DialogContent>
<DialogHeader>
<DialogTitle>Are you sure?</DialogTitle>
<DialogDescription>
This will permanently delete the risk &quot;{riskToDelete?.name}
&quot;. This action cannot be undone.
</DialogDescription>
</DialogHeader>
<DialogFooter>
<Button
variant="outline"
onClick={() => setRiskToDelete(null)}
disabled={isDeleting}
>
Cancel
</Button>
<Button
onClick={handleDeleteRisk}
disabled={isDeleting}
variant="destructive"
>
{isDeleting ? "Deleting..." : "Delete"}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</div>
</PageTemplate>
);
}
export default function RiskListView() {
const [searchParams] = useSearchParams();
const [queryRef, loadQuery] =
useQueryLoader<RiskListViewQuery>(riskListViewQuery);
const { organizationId } = useParams();
useEffect(() => {
const after = searchParams.get("after");
const before = searchParams.get("before");
loadQuery({
organizationId: organizationId!,
first: before ? undefined : defaultPageSize,
after: after || undefined,
last: before ? defaultPageSize : undefined,
before: before || undefined,
});
}, [loadQuery, organizationId]);
if (!queryRef) {
return <RiskViewSkeleton />;
}
return (
<Suspense fallback={<RiskViewSkeleton />}>
<RiskListViewContent queryRef={queryRef} />
</Suspense>
);
}

View File

@@ -0,0 +1,192 @@
/**
* @generated SignedSource<<c3a526641ca53f4ef760eacadc5c3685>>
* @lightSyntaxTransform
* @nogrep
*/
/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
import { ConcreteRequest } from 'relay-runtime';
export type CreateRiskInput = {
description: string;
name: string;
organizationId: string;
};
export type NewRiskViewCreateRiskMutation$variables = {
connections: ReadonlyArray<string>;
input: CreateRiskInput;
};
export type NewRiskViewCreateRiskMutation$data = {
readonly createRisk: {
readonly riskEdge: {
readonly node: {
readonly createdAt: string;
readonly description: string;
readonly id: string;
readonly name: string;
readonly updatedAt: string;
};
};
};
};
export type NewRiskViewCreateRiskMutation = {
response: NewRiskViewCreateRiskMutation$data;
variables: NewRiskViewCreateRiskMutation$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": "RiskEdge",
"kind": "LinkedField",
"name": "riskEdge",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "Risk",
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "id",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "name",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "description",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "createdAt",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "updatedAt",
"storageKey": null
}
],
"storageKey": null
}
],
"storageKey": null
};
return {
"fragment": {
"argumentDefinitions": [
(v0/*: any*/),
(v1/*: any*/)
],
"kind": "Fragment",
"metadata": null,
"name": "NewRiskViewCreateRiskMutation",
"selections": [
{
"alias": null,
"args": (v2/*: any*/),
"concreteType": "CreateRiskPayload",
"kind": "LinkedField",
"name": "createRisk",
"plural": false,
"selections": [
(v3/*: any*/)
],
"storageKey": null
}
],
"type": "Mutation",
"abstractKey": null
},
"kind": "Request",
"operation": {
"argumentDefinitions": [
(v1/*: any*/),
(v0/*: any*/)
],
"kind": "Operation",
"name": "NewRiskViewCreateRiskMutation",
"selections": [
{
"alias": null,
"args": (v2/*: any*/),
"concreteType": "CreateRiskPayload",
"kind": "LinkedField",
"name": "createRisk",
"plural": false,
"selections": [
(v3/*: any*/),
{
"alias": null,
"args": null,
"filters": null,
"handle": "prependEdge",
"key": "",
"kind": "LinkedHandle",
"name": "riskEdge",
"handleArgs": [
{
"kind": "Variable",
"name": "connections",
"variableName": "connections"
}
]
}
],
"storageKey": null
}
]
},
"params": {
"cacheID": "ce3ac503734a6f0eb26f88813ed02858",
"id": null,
"metadata": {},
"name": "NewRiskViewCreateRiskMutation",
"operationKind": "mutation",
"text": "mutation NewRiskViewCreateRiskMutation(\n $input: CreateRiskInput!\n) {\n createRisk(input: $input) {\n riskEdge {\n node {\n id\n name\n description\n createdAt\n updatedAt\n }\n }\n }\n}\n"
}
};
})();
(node as any).hash = "c0d8d1f046ed41c7a429c54b32358d3f";
export default node;

View File

@@ -0,0 +1,132 @@
/**
* @generated SignedSource<<349d62a3a4ed3ea1d83c12f0c61437dd>>
* @lightSyntaxTransform
* @nogrep
*/
/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
import { ConcreteRequest } from 'relay-runtime';
export type DeleteRiskInput = {
riskId: string;
};
export type RiskListViewDeleteMutation$variables = {
connections: ReadonlyArray<string>;
input: DeleteRiskInput;
};
export type RiskListViewDeleteMutation$data = {
readonly deleteRisk: {
readonly deletedRiskId: string;
};
};
export type RiskListViewDeleteMutation = {
response: RiskListViewDeleteMutation$data;
variables: RiskListViewDeleteMutation$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,
"kind": "ScalarField",
"name": "deletedRiskId",
"storageKey": null
};
return {
"fragment": {
"argumentDefinitions": [
(v0/*: any*/),
(v1/*: any*/)
],
"kind": "Fragment",
"metadata": null,
"name": "RiskListViewDeleteMutation",
"selections": [
{
"alias": null,
"args": (v2/*: any*/),
"concreteType": "DeleteRiskPayload",
"kind": "LinkedField",
"name": "deleteRisk",
"plural": false,
"selections": [
(v3/*: any*/)
],
"storageKey": null
}
],
"type": "Mutation",
"abstractKey": null
},
"kind": "Request",
"operation": {
"argumentDefinitions": [
(v1/*: any*/),
(v0/*: any*/)
],
"kind": "Operation",
"name": "RiskListViewDeleteMutation",
"selections": [
{
"alias": null,
"args": (v2/*: any*/),
"concreteType": "DeleteRiskPayload",
"kind": "LinkedField",
"name": "deleteRisk",
"plural": false,
"selections": [
(v3/*: any*/),
{
"alias": null,
"args": null,
"filters": null,
"handle": "deleteEdge",
"key": "",
"kind": "ScalarHandle",
"name": "deletedRiskId",
"handleArgs": [
{
"kind": "Variable",
"name": "connections",
"variableName": "connections"
}
]
}
],
"storageKey": null
}
]
},
"params": {
"cacheID": "525011737d0920883b3942d2603dc31f",
"id": null,
"metadata": {},
"name": "RiskListViewDeleteMutation",
"operationKind": "mutation",
"text": "mutation RiskListViewDeleteMutation(\n $input: DeleteRiskInput!\n) {\n deleteRisk(input: $input) {\n deletedRiskId\n }\n}\n"
}
};
})();
(node as any).hash = "4aee92e5abfd6733b44588bf01276560";
export default node;

View File

@@ -0,0 +1,309 @@
/**
* @generated SignedSource<<56b70bbecd785111cec0e7ff6ec2c226>>
* @lightSyntaxTransform
* @nogrep
*/
/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
import { ConcreteRequest } from 'relay-runtime';
import { FragmentRefs } from "relay-runtime";
export type RiskListViewPaginationQuery$variables = {
after?: string | null | undefined;
before?: string | null | undefined;
first?: number | null | undefined;
id: string;
last?: number | null | undefined;
};
export type RiskListViewPaginationQuery$data = {
readonly node: {
readonly " $fragmentSpreads": FragmentRefs<"RiskListView_risks">;
};
};
export type RiskListViewPaginationQuery = {
response: RiskListViewPaginationQuery$data;
variables: RiskListViewPaginationQuery$variables;
};
const node: ConcreteRequest = (function(){
var v0 = {
"defaultValue": null,
"kind": "LocalArgument",
"name": "after"
},
v1 = {
"defaultValue": null,
"kind": "LocalArgument",
"name": "before"
},
v2 = {
"defaultValue": null,
"kind": "LocalArgument",
"name": "first"
},
v3 = {
"defaultValue": null,
"kind": "LocalArgument",
"name": "id"
},
v4 = {
"defaultValue": null,
"kind": "LocalArgument",
"name": "last"
},
v5 = [
{
"kind": "Variable",
"name": "id",
"variableName": "id"
}
],
v6 = [
{
"kind": "Variable",
"name": "after",
"variableName": "after"
},
{
"kind": "Variable",
"name": "before",
"variableName": "before"
},
{
"kind": "Variable",
"name": "first",
"variableName": "first"
},
{
"kind": "Variable",
"name": "last",
"variableName": "last"
}
],
v7 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "__typename",
"storageKey": null
},
v8 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "id",
"storageKey": null
};
return {
"fragment": {
"argumentDefinitions": [
(v0/*: any*/),
(v1/*: any*/),
(v2/*: any*/),
(v3/*: any*/),
(v4/*: any*/)
],
"kind": "Fragment",
"metadata": null,
"name": "RiskListViewPaginationQuery",
"selections": [
{
"alias": null,
"args": (v5/*: any*/),
"concreteType": null,
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
{
"args": (v6/*: any*/),
"kind": "FragmentSpread",
"name": "RiskListView_risks"
}
],
"storageKey": null
}
],
"type": "Query",
"abstractKey": null
},
"kind": "Request",
"operation": {
"argumentDefinitions": [
(v0/*: any*/),
(v1/*: any*/),
(v2/*: any*/),
(v4/*: any*/),
(v3/*: any*/)
],
"kind": "Operation",
"name": "RiskListViewPaginationQuery",
"selections": [
{
"alias": null,
"args": (v5/*: any*/),
"concreteType": null,
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
(v7/*: any*/),
(v8/*: any*/),
{
"kind": "InlineFragment",
"selections": [
{
"alias": null,
"args": (v6/*: any*/),
"concreteType": "RiskConnection",
"kind": "LinkedField",
"name": "risks",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "RiskEdge",
"kind": "LinkedField",
"name": "edges",
"plural": true,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "Risk",
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
(v8/*: any*/),
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "name",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "description",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "createdAt",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "updatedAt",
"storageKey": null
},
(v7/*: any*/)
],
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "cursor",
"storageKey": null
}
],
"storageKey": null
},
{
"alias": null,
"args": null,
"concreteType": "PageInfo",
"kind": "LinkedField",
"name": "pageInfo",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "hasNextPage",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "hasPreviousPage",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "startCursor",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "endCursor",
"storageKey": null
}
],
"storageKey": null
},
{
"kind": "ClientExtension",
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "__id",
"storageKey": null
}
]
}
],
"storageKey": null
},
{
"alias": null,
"args": (v6/*: any*/),
"filters": null,
"handle": "connection",
"key": "RiskListView_risks",
"kind": "LinkedHandle",
"name": "risks"
}
],
"type": "Organization",
"abstractKey": null
}
],
"storageKey": null
}
]
},
"params": {
"cacheID": "ea3c50c98dea1cce588bbb0e8705cbc4",
"id": null,
"metadata": {},
"name": "RiskListViewPaginationQuery",
"operationKind": "query",
"text": "query RiskListViewPaginationQuery(\n $after: CursorKey\n $before: CursorKey\n $first: Int\n $last: Int\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...RiskListView_risks_pbnwq\n id\n }\n}\n\nfragment RiskListView_risks_pbnwq on Organization {\n risks(first: $first, after: $after, last: $last, before: $before) {\n edges {\n node {\n id\n name\n description\n createdAt\n updatedAt\n __typename\n }\n cursor\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n id\n}\n"
}
};
})();
(node as any).hash = "4a665835c97d7b93725e8bbe708d7cfb";
export default node;

View File

@@ -0,0 +1,311 @@
/**
* @generated SignedSource<<2812fb6e9c5db33d4c616daf9a11b012>>
* @lightSyntaxTransform
* @nogrep
*/
/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
import { ConcreteRequest } from 'relay-runtime';
import { FragmentRefs } from "relay-runtime";
export type RiskListViewQuery$variables = {
after?: string | null | undefined;
before?: string | null | undefined;
first?: number | null | undefined;
last?: number | null | undefined;
organizationId: string;
};
export type RiskListViewQuery$data = {
readonly organization: {
readonly id: string;
readonly " $fragmentSpreads": FragmentRefs<"RiskListView_risks">;
};
};
export type RiskListViewQuery = {
response: RiskListViewQuery$data;
variables: RiskListViewQuery$variables;
};
const node: ConcreteRequest = (function(){
var v0 = {
"defaultValue": null,
"kind": "LocalArgument",
"name": "after"
},
v1 = {
"defaultValue": null,
"kind": "LocalArgument",
"name": "before"
},
v2 = {
"defaultValue": null,
"kind": "LocalArgument",
"name": "first"
},
v3 = {
"defaultValue": null,
"kind": "LocalArgument",
"name": "last"
},
v4 = {
"defaultValue": null,
"kind": "LocalArgument",
"name": "organizationId"
},
v5 = [
{
"kind": "Variable",
"name": "id",
"variableName": "organizationId"
}
],
v6 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "id",
"storageKey": null
},
v7 = [
{
"kind": "Variable",
"name": "after",
"variableName": "after"
},
{
"kind": "Variable",
"name": "before",
"variableName": "before"
},
{
"kind": "Variable",
"name": "first",
"variableName": "first"
},
{
"kind": "Variable",
"name": "last",
"variableName": "last"
}
],
v8 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "__typename",
"storageKey": null
};
return {
"fragment": {
"argumentDefinitions": [
(v0/*: any*/),
(v1/*: any*/),
(v2/*: any*/),
(v3/*: any*/),
(v4/*: any*/)
],
"kind": "Fragment",
"metadata": null,
"name": "RiskListViewQuery",
"selections": [
{
"alias": "organization",
"args": (v5/*: any*/),
"concreteType": null,
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
(v6/*: any*/),
{
"args": (v7/*: any*/),
"kind": "FragmentSpread",
"name": "RiskListView_risks"
}
],
"storageKey": null
}
],
"type": "Query",
"abstractKey": null
},
"kind": "Request",
"operation": {
"argumentDefinitions": [
(v4/*: any*/),
(v2/*: any*/),
(v0/*: any*/),
(v3/*: any*/),
(v1/*: any*/)
],
"kind": "Operation",
"name": "RiskListViewQuery",
"selections": [
{
"alias": "organization",
"args": (v5/*: any*/),
"concreteType": null,
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
(v8/*: any*/),
(v6/*: any*/),
{
"kind": "InlineFragment",
"selections": [
{
"alias": null,
"args": (v7/*: any*/),
"concreteType": "RiskConnection",
"kind": "LinkedField",
"name": "risks",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "RiskEdge",
"kind": "LinkedField",
"name": "edges",
"plural": true,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "Risk",
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
(v6/*: any*/),
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "name",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "description",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "createdAt",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "updatedAt",
"storageKey": null
},
(v8/*: any*/)
],
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "cursor",
"storageKey": null
}
],
"storageKey": null
},
{
"alias": null,
"args": null,
"concreteType": "PageInfo",
"kind": "LinkedField",
"name": "pageInfo",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "hasNextPage",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "hasPreviousPage",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "startCursor",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "endCursor",
"storageKey": null
}
],
"storageKey": null
},
{
"kind": "ClientExtension",
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "__id",
"storageKey": null
}
]
}
],
"storageKey": null
},
{
"alias": null,
"args": (v7/*: any*/),
"filters": null,
"handle": "connection",
"key": "RiskListView_risks",
"kind": "LinkedHandle",
"name": "risks"
}
],
"type": "Organization",
"abstractKey": null
}
],
"storageKey": null
}
]
},
"params": {
"cacheID": "7b0cab0d71217df4a49ea2245b2705a8",
"id": null,
"metadata": {},
"name": "RiskListViewQuery",
"operationKind": "query",
"text": "query RiskListViewQuery(\n $organizationId: ID!\n $first: Int\n $after: CursorKey\n $last: Int\n $before: CursorKey\n) {\n organization: node(id: $organizationId) {\n __typename\n id\n ...RiskListView_risks_pbnwq\n }\n}\n\nfragment RiskListView_risks_pbnwq on Organization {\n risks(first: $first, after: $after, last: $last, before: $before) {\n edges {\n node {\n id\n name\n description\n createdAt\n updatedAt\n __typename\n }\n cursor\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n id\n}\n"
}
};
})();
(node as any).hash = "0fff20fd656047e6df52818ccf0a0f3b";
export default node;

View File

@@ -0,0 +1,244 @@
/**
* @generated SignedSource<<5cb8bd63e484e9c8d842ddf05618d475>>
* @lightSyntaxTransform
* @nogrep
*/
/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
import { ReaderFragment } from 'relay-runtime';
import { FragmentRefs } from "relay-runtime";
export type RiskListView_risks$data = {
readonly id: string;
readonly risks: {
readonly __id: string;
readonly edges: ReadonlyArray<{
readonly node: {
readonly createdAt: string;
readonly description: string;
readonly id: string;
readonly name: string;
readonly updatedAt: string;
};
}>;
readonly pageInfo: {
readonly endCursor: string | null | undefined;
readonly hasNextPage: boolean;
readonly hasPreviousPage: boolean;
readonly startCursor: string | null | undefined;
};
};
readonly " $fragmentType": "RiskListView_risks";
};
export type RiskListView_risks$key = {
readonly " $data"?: RiskListView_risks$data;
readonly " $fragmentSpreads": FragmentRefs<"RiskListView_risks">;
};
const node: ReaderFragment = (function(){
var v0 = [
"risks"
],
v1 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "id",
"storageKey": null
};
return {
"argumentDefinitions": [
{
"defaultValue": null,
"kind": "LocalArgument",
"name": "after"
},
{
"defaultValue": null,
"kind": "LocalArgument",
"name": "before"
},
{
"defaultValue": null,
"kind": "LocalArgument",
"name": "first"
},
{
"defaultValue": null,
"kind": "LocalArgument",
"name": "last"
}
],
"kind": "Fragment",
"metadata": {
"connection": [
{
"count": null,
"cursor": null,
"direction": "bidirectional",
"path": (v0/*: any*/)
}
],
"refetch": {
"connection": {
"forward": {
"count": "first",
"cursor": "after"
},
"backward": {
"count": "last",
"cursor": "before"
},
"path": (v0/*: any*/)
},
"fragmentPathInResult": [
"node"
],
"operation": require('./RiskListViewPaginationQuery.graphql'),
"identifierInfo": {
"identifierField": "id",
"identifierQueryVariableName": "id"
}
}
},
"name": "RiskListView_risks",
"selections": [
{
"alias": "risks",
"args": null,
"concreteType": "RiskConnection",
"kind": "LinkedField",
"name": "__RiskListView_risks_connection",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "RiskEdge",
"kind": "LinkedField",
"name": "edges",
"plural": true,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "Risk",
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
(v1/*: any*/),
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "name",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "description",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "createdAt",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "updatedAt",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "__typename",
"storageKey": null
}
],
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "cursor",
"storageKey": null
}
],
"storageKey": null
},
{
"alias": null,
"args": null,
"concreteType": "PageInfo",
"kind": "LinkedField",
"name": "pageInfo",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "hasNextPage",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "hasPreviousPage",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "startCursor",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "endCursor",
"storageKey": null
}
],
"storageKey": null
},
{
"kind": "ClientExtension",
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "__id",
"storageKey": null
}
]
}
],
"storageKey": null
},
(v1/*: any*/)
],
"type": "Organization",
"abstractKey": null
};
})();
(node as any).hash = "4a665835c97d7b93725e8bbe708d7cfb";
export default node;