Rename policy to document

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
Sacha Al Himdani
2025-05-29 19:58:23 -07:00
parent c9f2df3cdd
commit e1b4079e1f
76 changed files with 5572 additions and 4690 deletions

View File

@@ -13,7 +13,7 @@
@import "@radix-ui/colors/orange.css";
@import "@radix-ui/colors/orange-dark.css";
@import "./styles/policy-content.css";
@import "./styles/document-content.css";
@plugin 'tailwindcss-animate';
@plugin "@tailwindcss/typography";

View File

@@ -7,7 +7,7 @@ import { HelmetProvider } from "react-helmet-async";
import { RelayEnvironmentProvider } from "react-relay";
import { BrowserRouter, Route, Routes, useLocation } from "react-router";
import "./App.css";
import "./styles/policy-content.css";
import "./styles/document-content.css";
import ErrorBoundary from "./components/ErrorBoundary";
import AuthLayout from "./layouts/AuthLayout";
import { RelayEnvironment } from "./RelayEnvironment";
@@ -55,7 +55,7 @@ function App() {
/>
<Route
path="policies/signing-requests"
path="documents/signing-requests"
element={<SigningRequestsPage />}
/>

View File

@@ -1,4 +1,4 @@
.policy-editor-container {
.document-editor-container {
border-radius: 0.5rem;
position: relative;
line-height: 1.5;
@@ -9,7 +9,7 @@
width: 100%;
}
.policy-editor {
.document-editor {
background: var(--bg-invert-bg);
position: relative;
border-radius: 0.5rem;
@@ -18,7 +18,7 @@
width: 100%;
}
.policy-editor-inner {
.document-editor-inner {
background: #fff;
position: relative;
border-radius: 0 0 0.5rem 0.5rem;
@@ -26,7 +26,7 @@
min-height: 250px;
}
.policy-editor-input {
.document-editor-input {
min-height: 250px;
max-height: 500px;
resize: none;
@@ -41,7 +41,7 @@
color: #000;
}
.policy-editor-placeholder {
.document-editor-placeholder {
color: #999;
overflow: hidden;
position: absolute;
@@ -54,12 +54,12 @@
pointer-events: none;
}
.policy-editor-paragraph {
.document-editor-paragraph {
margin: 0 0 15px 0;
position: relative;
}
.policy-editor .toolbar {
.document-editor .toolbar {
display: flex;
flex-wrap: wrap;
padding: 8px;
@@ -68,7 +68,7 @@
border-radius: 0.5rem 0.5rem 0 0;
}
.policy-editor .toolbar button {
.document-editor .toolbar button {
border: 0;
display: flex;
background: none;
@@ -80,15 +80,15 @@
justify-content: center;
}
.policy-editor .toolbar button:hover {
.document-editor .toolbar button:hover {
background-color: rgba(0, 0, 0, 0.05);
}
.policy-editor .toolbar button.active {
.document-editor .toolbar button.active {
background-color: rgba(0, 0, 0, 0.1);
}
.policy-editor .toolbar .divider {
.document-editor .toolbar .divider {
width: 1px;
background-color: hsl(var(--solid-b));
margin: 0 8px;
@@ -281,4 +281,4 @@ ol.editor-list-ol ol.editor-list-ol > .editor-listitem {
.editor-tokenFunction {
color: #dd4a68;
}
}

View File

@@ -174,9 +174,9 @@ function getNavItems(organizationId?: string): NavItem[] {
icon: Store,
},
{
title: "Policies",
title: "Documents",
url: organizationId
? `/organizations/${organizationId}/policies`
? `/organizations/${organizationId}/documents`
: undefined,
icon: FileText,
},

View File

@@ -17,7 +17,7 @@ import remarkGfm from "remark-gfm";
import rehypeRaw from 'rehype-raw';
type Document = {
policy_version_id: string;
document_version_id: string;
title: string;
content: string;
signed?: boolean;
@@ -49,7 +49,7 @@ export default function SigningRequestsPage() {
async function fetchDocuments() {
try {
const response = await fetch(
buildEndpoint("/api/console/v1/policies/signing-requests"),
buildEndpoint("/api/console/v1/documents/signing-requests"),
{
method: "GET",
headers: {
@@ -96,7 +96,7 @@ export default function SigningRequestsPage() {
try {
const response = await fetch(
buildEndpoint(
`/api/console/v1/policies/signing-requests/${docToSign.policy_version_id}/sign`,
`/api/console/v1/documents/signing-requests/${docToSign.document_version_id}/sign`,
),
{
method: "POST",

View File

@@ -13,7 +13,7 @@ import { NavLink, Outlet, Route, Routes, To, useParams } from "react-router";
import { OrganizationBreadcrumbBreadcrumbFrameworkOverviewQuery } from "./__generated__/OrganizationBreadcrumbBreadcrumbFrameworkOverviewQuery.graphql";
import { OrganizationBreadcrumbBreadcrumbPeopleOverviewQuery } from "./__generated__/OrganizationBreadcrumbBreadcrumbPeopleOverviewQuery.graphql";
import { OrganizationBreadcrumbBreadcrumbPolicyOverviewQuery } from "./__generated__/OrganizationBreadcrumbBreadcrumbPolicyOverviewQuery.graphql";
import { OrganizationBreadcrumbBreadcrumbDocumentOverviewQuery } from "./__generated__/OrganizationBreadcrumbBreadcrumbDocumentOverviewQuery.graphql";
import { OrganizationBreadcrumbBreadcrumbVendorOverviewQuery } from "./__generated__/OrganizationBreadcrumbBreadcrumbVendorOverviewQuery.graphql";
import { OrganizationBreadcrumbOrganizationQuery } from "./__generated__/OrganizationBreadcrumbOrganizationQuery.graphql";
import { OrganizationBreadcrumbBreadcrumbMeasureViewQuery } from "./__generated__/OrganizationBreadcrumbBreadcrumbMeasureViewQuery.graphql";
@@ -264,14 +264,14 @@ function BreadcrumbPeopleOverview() {
);
}
function BreadcrumbPolicyList() {
function BreadcrumbDocumentList() {
const { organizationId } = useParams();
return (
<>
<BreadcrumbSeparator />
<BreadcrumbItem>
<BreadcrumbNavLink to={`/organizations/${organizationId}/policies`}>
Policies
<BreadcrumbNavLink to={`/organizations/${organizationId}/documents`}>
Documents
</BreadcrumbNavLink>
</BreadcrumbItem>
<Outlet />
@@ -279,23 +279,23 @@ function BreadcrumbPolicyList() {
);
}
function BreadcrumbPolicyOverview() {
const { organizationId, policyId } = useParams();
function BreadcrumbDocumentOverview() {
const { organizationId, documentId } = useParams();
const data =
useLazyLoadQuery<OrganizationBreadcrumbBreadcrumbPolicyOverviewQuery>(
useLazyLoadQuery<OrganizationBreadcrumbBreadcrumbDocumentOverviewQuery>(
graphql`
query OrganizationBreadcrumbBreadcrumbPolicyOverviewQuery(
$policyId: ID!
query OrganizationBreadcrumbBreadcrumbDocumentOverviewQuery(
$documentId: ID!
) {
policy: node(id: $policyId) {
document: node(id: $documentId) {
id
... on Policy {
... on Document {
title
}
}
}
`,
{ policyId: policyId! },
{ documentId: documentId! },
{ fetchPolicy: "store-or-network" },
);
@@ -304,9 +304,9 @@ function BreadcrumbPolicyOverview() {
<BreadcrumbSeparator />
<BreadcrumbItem>
<BreadcrumbNavLink
to={`/organizations/${organizationId}/policies/${policyId}`}
to={`/organizations/${organizationId}/documents/${documentId}`}
>
{data.policy?.title}
{data.document?.title}
</BreadcrumbNavLink>
</BreadcrumbItem>
<Outlet />
@@ -531,12 +531,12 @@ export function BreadCrumb() {
}
/>
</Route>
<Route path="policies" element={<BreadcrumbPolicyList />}>
<Route path="documents" element={<BreadcrumbDocumentList />}>
<Route
path=":policyId"
path=":documentId"
element={
<Suspense>
<BreadcrumbPolicyOverview />
<BreadcrumbDocumentOverview />
</Suspense>
}
>

View File

@@ -18,9 +18,9 @@ import { NewMeasurePage } from "./measures/NewMeasurePage";
import { NewPeoplePage } from "./people/NewPeoplePage";
import { PeopleListPage } from "./people/PeopleListPage";
import { PeoplePage } from "./people/PeoplePage";
import { EditPolicyPage } from "./policies/EditPolicyPage";
import { PolicyListPage } from "./policies/PolicyListPage";
import { ShowPolicyPage } from "./policies/ShowPolicyPage";
import { EditDocumentPage } from "./documents/EditDocumentPage";
import { DocumentListPage } from "./documents/DocumentListPage";
import { ShowDocumentPage } from "./documents/ShowDocumentPage";
import { EditRiskPage } from "./risks/EditRiskPage";
import { NewRiskPage } from "./risks/NewRiskPage";
import { ListRiskPage } from "./risks/ListRiskPage";
@@ -54,11 +54,11 @@ export function OrganizationsRoutes() {
<Route path="measures/:measureId" element={<MeasurePage />} />
<Route path="measures/:measureId/edit" element={<EditMeasurePage />} />
<Route path="vendors/:vendorId" element={<VendorPage />} />
<Route path="policies" element={<PolicyListPage />} />
<Route path="policies/:policyId" element={<ShowPolicyPage />} />
<Route path="documents" element={<DocumentListPage />} />
<Route path="documents/:documentId" element={<ShowDocumentPage />} />
<Route
path="policies/:policyId/versions/:versionId/edit"
element={<EditPolicyPage />}
path="documents/:documentId/versions/:versionId/edit"
element={<EditDocumentPage />}
/>
<Route path="risks" element={<ListRiskPage />} />
<Route path="risks/new" element={<NewRiskPage />} />

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<ca410ce7df7231383e55d134d37916bd>>
* @generated SignedSource<<2cac1fe037796fc1b9542c9388ed930d>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -9,18 +9,18 @@
// @ts-nocheck
import { ConcreteRequest } from 'relay-runtime';
export type OrganizationBreadcrumbBreadcrumbPolicyOverviewQuery$variables = {
policyId: string;
export type OrganizationBreadcrumbBreadcrumbDocumentOverviewQuery$variables = {
documentId: string;
};
export type OrganizationBreadcrumbBreadcrumbPolicyOverviewQuery$data = {
readonly policy: {
export type OrganizationBreadcrumbBreadcrumbDocumentOverviewQuery$data = {
readonly document: {
readonly id: string;
readonly title?: string;
};
};
export type OrganizationBreadcrumbBreadcrumbPolicyOverviewQuery = {
response: OrganizationBreadcrumbBreadcrumbPolicyOverviewQuery$data;
variables: OrganizationBreadcrumbBreadcrumbPolicyOverviewQuery$variables;
export type OrganizationBreadcrumbBreadcrumbDocumentOverviewQuery = {
response: OrganizationBreadcrumbBreadcrumbDocumentOverviewQuery$data;
variables: OrganizationBreadcrumbBreadcrumbDocumentOverviewQuery$variables;
};
const node: ConcreteRequest = (function(){
@@ -28,14 +28,14 @@ var v0 = [
{
"defaultValue": null,
"kind": "LocalArgument",
"name": "policyId"
"name": "documentId"
}
],
v1 = [
{
"kind": "Variable",
"name": "id",
"variableName": "policyId"
"variableName": "documentId"
}
],
v2 = {
@@ -56,7 +56,7 @@ v3 = {
"storageKey": null
}
],
"type": "Policy",
"type": "Document",
"abstractKey": null
};
return {
@@ -64,10 +64,10 @@ return {
"argumentDefinitions": (v0/*: any*/),
"kind": "Fragment",
"metadata": null,
"name": "OrganizationBreadcrumbBreadcrumbPolicyOverviewQuery",
"name": "OrganizationBreadcrumbBreadcrumbDocumentOverviewQuery",
"selections": [
{
"alias": "policy",
"alias": "document",
"args": (v1/*: any*/),
"concreteType": null,
"kind": "LinkedField",
@@ -87,10 +87,10 @@ return {
"operation": {
"argumentDefinitions": (v0/*: any*/),
"kind": "Operation",
"name": "OrganizationBreadcrumbBreadcrumbPolicyOverviewQuery",
"name": "OrganizationBreadcrumbBreadcrumbDocumentOverviewQuery",
"selections": [
{
"alias": "policy",
"alias": "document",
"args": (v1/*: any*/),
"concreteType": null,
"kind": "LinkedField",
@@ -112,16 +112,16 @@ return {
]
},
"params": {
"cacheID": "bc0b04bbe75c94660de230461532773a",
"cacheID": "bf577e8122cd59894e36f951f7a3551a",
"id": null,
"metadata": {},
"name": "OrganizationBreadcrumbBreadcrumbPolicyOverviewQuery",
"name": "OrganizationBreadcrumbBreadcrumbDocumentOverviewQuery",
"operationKind": "query",
"text": "query OrganizationBreadcrumbBreadcrumbPolicyOverviewQuery(\n $policyId: ID!\n) {\n policy: node(id: $policyId) {\n __typename\n id\n ... on Policy {\n title\n }\n }\n}\n"
"text": "query OrganizationBreadcrumbBreadcrumbDocumentOverviewQuery(\n $documentId: ID!\n) {\n document: node(id: $documentId) {\n __typename\n id\n ... on Document {\n title\n }\n }\n}\n"
}
};
})();
(node as any).hash = "ced380d1e6eb1c31ecc5f2dc9096133b";
(node as any).hash = "7ef8052c69e997cf2252d7c00b3f1c1f";
export default node;

View File

@@ -5,13 +5,13 @@ import { useLocation } from "react-router";
import { ErrorBoundaryWithLocation } from "../ErrorBoundary";
import { lazy } from "@probo/react-lazy";
const PolicyListView = lazy(() => import("./PolicyListView"));
const DocumentListView = lazy(() => import("./DocumentListView"));
export function PolicyListViewSkeleton() {
export function DocumentListViewSkeleton() {
return (
<PageTemplateSkeleton
title="Policies"
description="Manage your organization's policies"
title="Documents"
description="Manage your organization's documents"
actions={
<div className="bg-subtle-bg animate-pulse h-9 w-1/6 rounded-lg" />
}
@@ -28,7 +28,7 @@ export function PolicyListViewSkeleton() {
{/* Results summary skeleton */}
<div className="mb-4 h-4 w-48 bg-subtle-bg animate-pulse rounded" />
{/* Policy grid skeleton */}
{/* Document grid skeleton */}
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
{[1, 2, 3, 4, 5, 6].map((i) => (
<Card key={i} className="bg-level-1/50 h-full flex flex-col">
@@ -60,13 +60,13 @@ export function PolicyListViewSkeleton() {
);
}
export function PolicyListPage() {
export function DocumentListPage() {
const location = useLocation();
return (
<Suspense key={location.pathname} fallback={<PolicyListViewSkeleton />}>
<Suspense key={location.pathname} fallback={<DocumentListViewSkeleton />}>
<ErrorBoundaryWithLocation>
<PolicyListView />
<DocumentListView />
</ErrorBoundaryWithLocation>
</Suspense>
);

View File

@@ -27,14 +27,14 @@ import {
import { useToast } from "@/hooks/use-toast";
import { format } from "date-fns";
import type {
PolicyListViewQuery,
PolicyListViewQuery$data,
} from "./__generated__/PolicyListViewQuery.graphql";
import type { PolicyListViewDeleteMutation } from "./__generated__/PolicyListViewDeleteMutation.graphql";
import type { PolicyListViewCreateMutation } from "./__generated__/PolicyListViewCreateMutation.graphql";
import type { PolicyListViewSendSigningNotificationsMutation } from "./__generated__/PolicyListViewSendSigningNotificationsMutation.graphql";
DocumentListViewQuery,
DocumentListViewQuery$data,
} from "./__generated__/DocumentListViewQuery.graphql";
import type { DocumentListViewDeleteMutation } from "./__generated__/DocumentListViewDeleteMutation.graphql";
import type { DocumentListViewCreateMutation } from "./__generated__/DocumentListViewCreateMutation.graphql";
import type { DocumentListViewSendSigningNotificationsMutation } from "./__generated__/DocumentListViewSendSigningNotificationsMutation.graphql";
import { PageTemplate } from "@/components/PageTemplate";
import { PolicyListViewSkeleton } from "./PolicyListPage";
import { DocumentListViewSkeleton } from "./DocumentListPage";
import {
Dialog,
DialogContent,
@@ -46,8 +46,8 @@ import PeopleSelector from "@/components/PeopleSelector";
import type { PeopleSelector_organization$key } from "@/components/__generated__/PeopleSelector_organization.graphql";
import { Textarea } from "@/components/ui/textarea";
const policyListViewQuery = graphql`
query PolicyListViewQuery($organizationId: ID!) {
const documentListViewQuery = graphql`
query DocumentListViewQuery($organizationId: ID!) {
viewer {
user {
id
@@ -57,8 +57,8 @@ const policyListViewQuery = graphql`
organization: node(id: $organizationId) {
... on Organization {
...PeopleSelector_organization
policies(first: 50, orderBy: { field: TITLE, direction: ASC })
@connection(key: "PolicyListView_policies") {
documents(first: 50, orderBy: { field: TITLE, direction: ASC })
@connection(key: "DocumentListView_documents") {
edges {
node {
id
@@ -96,24 +96,24 @@ const policyListViewQuery = graphql`
}
`;
const DeletePolicyMutation = graphql`
mutation PolicyListViewDeleteMutation(
$input: DeletePolicyInput!
const DeleteDocumentMutation = graphql`
mutation DocumentListViewDeleteMutation(
$input: DeleteDocumentInput!
$connections: [ID!]!
) {
deletePolicy(input: $input) {
deletedPolicyId @deleteEdge(connections: $connections)
deleteDocument(input: $input) {
deletedDocumentId @deleteEdge(connections: $connections)
}
}
`;
const createPolicyMutation = graphql`
mutation PolicyListViewCreateMutation(
$input: CreatePolicyInput!
const createDocumentMutation = graphql`
mutation DocumentListViewCreateMutation(
$input: CreateDocumentInput!
$connections: [ID!]!
) {
createPolicy(input: $input) {
policyEdge @prependEdge(connections: $connections) {
createDocument(input: $input) {
documentEdge @prependEdge(connections: $connections) {
node {
id
title
@@ -131,7 +131,7 @@ const createPolicyMutation = graphql`
`;
const sendSigningNotificationsMutation = graphql`
mutation PolicyListViewSendSigningNotificationsMutation(
mutation DocumentListViewSendSigningNotificationsMutation(
$input: SendSigningNotificationsInput!
) {
sendSigningNotifications(input: $input) {
@@ -139,21 +139,21 @@ const sendSigningNotificationsMutation = graphql`
}
}
`;
function PolicyTableRow({
policy,
function DocumentTableRow({
document,
organizationId,
}: {
policy: NonNullable<
PolicyListViewQuery$data["organization"]["policies"]
document: NonNullable<
DocumentListViewQuery$data["organization"]["documents"]
>["edges"][0]["node"];
organizationId: string;
}) {
const navigate = useNavigate();
const { toast } = useToast();
const [deletePolicy, isDeleting] =
useMutation<PolicyListViewDeleteMutation>(DeletePolicyMutation);
const [deleteDocument, isDeleting] =
useMutation<DocumentListViewDeleteMutation>(DeleteDocumentMutation);
const latestVersion = policy.versions?.edges[0]?.node;
const latestVersion = document.versions?.edges[0]?.node;
const status = latestVersion?.status || "DRAFT";
// Get signature counts
@@ -163,45 +163,45 @@ function PolicyTableRow({
(edge) => edge?.node?.state === "SIGNED",
).length;
const handleDeletePolicy = (e: React.MouseEvent) => {
const handleDeleteDocument = (e: React.MouseEvent) => {
e.stopPropagation();
if (
window.confirm(
"Are you sure you want to delete this policy? This action cannot be undone.",
"Are you sure you want to delete this document? This action cannot be undone.",
)
) {
deletePolicy({
deleteDocument({
variables: {
input: {
policyId: policy.id,
documentId: document.id,
},
connections: [
ConnectionHandler.getConnectionID(
organizationId,
"PolicyListView_policies",
"DocumentListView_documents",
),
],
},
onCompleted: (_, errors) => {
if (errors) {
console.error("Error deleting policy:", errors);
console.error("Error deleting document:", errors);
toast({
title: "Error",
description: "Failed to delete policy. Please try again.",
description: "Failed to delete document. Please try again.",
variant: "destructive",
});
return;
}
toast({
title: "Success",
description: "Policy deleted successfully.",
description: "Document deleted successfully.",
});
},
onError: (error) => {
console.error("Error deleting policy:", error);
console.error("Error deleting document:", error);
toast({
title: "Error",
description: "Failed to delete policy. Please try again.",
description: "Failed to delete document. Please try again.",
variant: "destructive",
});
},
@@ -211,32 +211,32 @@ function PolicyTableRow({
const handleView = (e: React.MouseEvent) => {
e.stopPropagation();
navigate(`/organizations/${organizationId}/policies/${policy.id}`);
navigate(`/organizations/${organizationId}/documents/${document.id}`);
};
return (
<tr
className="border-t border-solid-b hover:bg-subtle-bg cursor-pointer"
onClick={() => {
navigate(`/organizations/${organizationId}/policies/${policy.id}`);
navigate(`/organizations/${organizationId}/documents/${document.id}`);
}}
>
<td className="py-4 px-6">
<div className="flex flex-col">
<span className="font-medium text-primary">{policy.title}</span>
<span className="font-medium text-primary">{document.title}</span>
<span className="text-sm text-tertiary">
{policy.description || "No description provided"}
{document.description || "No description provided"}
</span>
</div>
</td>
<td className="py-4 px-6">
<span className="text-sm text-primary">
{policy.owner?.fullName || "Unassigned"}
{document.owner?.fullName || "Unassigned"}
</span>
</td>
<td className="py-4 px-6">
<span className="text-sm text-primary">
{format(new Date(policy.updatedAt), "MMM d, yyyy")}
{format(new Date(document.updatedAt), "MMM d, yyyy")}
</span>
</td>
<td className="py-4 px-6">
@@ -277,7 +277,7 @@ function PolicyTableRow({
View
</DropdownMenuItem>
<DropdownMenuItem
onClick={handleDeletePolicy}
onClick={handleDeleteDocument}
className="text-danger focus:text-danger focus:bg-danger-bg"
disabled={isDeleting}
>
@@ -291,7 +291,7 @@ function PolicyTableRow({
);
}
function CreatePolicyModal({
function CreateDocumentModal({
open,
onOpenChange,
organizationId,
@@ -307,8 +307,8 @@ function CreatePolicyModal({
const [content, setContent] = useState("");
const [ownerId, setOwnerId] = useState<string | null>(null);
const [createPolicy, isCreating] =
useMutation<PolicyListViewCreateMutation>(createPolicyMutation);
const [createDocument, isCreating] =
useMutation<DocumentListViewCreateMutation>(createDocumentMutation);
// Reset form fields
const resetForm = () => {
@@ -317,11 +317,11 @@ function CreatePolicyModal({
setOwnerId(null);
};
const handleCreatePolicy = () => {
const handleCreateDocument = () => {
if (!title.trim()) {
toast({
title: "Error",
description: "Please enter a policy title.",
description: "Please enter a document title.",
variant: "destructive",
});
return;
@@ -330,7 +330,7 @@ function CreatePolicyModal({
if (!ownerId) {
toast({
title: "Error",
description: "Please select an owner for the policy.",
description: "Please select an owner for the document.",
variant: "destructive",
});
return;
@@ -343,23 +343,23 @@ function CreatePolicyModal({
ownerId,
};
createPolicy({
createDocument({
variables: {
input,
connections: [
ConnectionHandler.getConnectionID(
organizationId,
"PolicyListView_policies",
"DocumentListView_documents",
{ orderBy: { field: "TITLE", direction: "ASC" } },
),
],
},
onCompleted: (response, errors) => {
if (errors) {
console.error("Error creating policy:", errors);
console.error("Error creating document:", errors);
toast({
title: "Error",
description: "Failed to create policy. Please try again.",
description: "Failed to create document. Please try again.",
variant: "destructive",
});
return;
@@ -367,17 +367,17 @@ function CreatePolicyModal({
toast({
title: "Success",
description: "Policy created successfully!",
description: "Document created successfully!",
});
resetForm();
onOpenChange(false);
},
onError: (error) => {
console.error("Error creating policy:", error);
console.error("Error creating document:", error);
toast({
title: "Error",
description: "Failed to create policy. Please try again.",
description: "Failed to create document. Please try again.",
variant: "destructive",
});
},
@@ -394,16 +394,16 @@ function CreatePolicyModal({
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="sm:max-w-[1080px] h-[700px] max-h-[700px] p-0 gap-0 flex flex-col">
<DialogTitle className="sr-only">Create new policy</DialogTitle>
<DialogTitle className="sr-only">Create new document</DialogTitle>
<DialogDescription className="sr-only">
Form to create a new policy with title, content, and owner
Form to create a new document with title, content, and owner
information.
</DialogDescription>
<div className="flex justify-between items-center py-2 px-4 border-b border-solid-b h-[40px]">
<div className="flex items-center gap-1 text-sm">
<span className="text-tertiary">Policies</span>
<span className="text-tertiary">Documents</span>
<ChevronDown className="h-3 w-3 text-quaternary rotate-[270deg]" />
<span className="font-medium">New policy</span>
<span className="font-medium">New document</span>
</div>
</div>
@@ -425,25 +425,25 @@ function CreatePolicyModal({
}
}}
onFocus={(e) => {
if (e.currentTarget.textContent === "Enter policy title...") {
if (e.currentTarget.textContent === "Enter document title...") {
e.currentTarget.textContent = "";
}
}}
onBlur={(e) => {
if (!e.currentTarget.textContent?.trim()) {
e.currentTarget.textContent = "Enter policy title...";
e.currentTarget.textContent = "Enter document title...";
setTitle("");
}
}}
ref={(el) => {
if (el && !el.textContent) {
el.textContent = title || "Enter policy title...";
el.textContent = title || "Enter document title...";
}
}}
></h1>
</div>
<Textarea
placeholder="This Privacy Policy outlines how NovaSoft collects, uses, and protects personal information provided by users of its services. By accessing or using our platform, you agree to the collection and use of information in accordance with this policy..."
placeholder="This Privacy Document outlines how NovaSoft collects, uses, and protects personal information provided by users of its services. By accessing or using our platform, you agree to the collection and use of information in accordance with this document..."
className="min-h-[300px] border-none resize-none p-0 focus-visible:ring-0 focus-visible:ring-offset-0"
value={content}
onChange={(e) => setContent(e.target.value)}
@@ -499,11 +499,11 @@ function CreatePolicyModal({
Cancel
</Button>
<Button
onClick={handleCreatePolicy}
onClick={handleCreateDocument}
disabled={isCreating}
className="h-9"
>
{isCreating ? "Creating..." : "Create policy"}
{isCreating ? "Creating..." : "Create document"}
</Button>
</DialogFooter>
</DialogContent>
@@ -511,24 +511,24 @@ function CreatePolicyModal({
);
}
function PolicyListViewContent({
function DocumentListViewContent({
queryRef,
}: {
queryRef: PreloadedQuery<PolicyListViewQuery>;
queryRef: PreloadedQuery<DocumentListViewQuery>;
}) {
const data = usePreloadedQuery<PolicyListViewQuery>(
policyListViewQuery,
const data = usePreloadedQuery<DocumentListViewQuery>(
documentListViewQuery,
queryRef,
);
const { organizationId } = useParams();
const policies =
data.organization?.policies?.edges.map((edge) => edge?.node) ?? [];
const documents =
data.organization?.documents?.edges.map((edge) => edge?.node) ?? [];
const [createModalOpen, setCreateModalOpen] = useState(false);
const [confirmSigningModalOpen, setConfirmSigningModalOpen] = useState(false);
const [sendSigningNotifications, isSendingNotifications] =
useMutation<PolicyListViewSendSigningNotificationsMutation>(
useMutation<DocumentListViewSendSigningNotificationsMutation>(
sendSigningNotificationsMutation,
);
const { toast } = useToast();
@@ -580,12 +580,12 @@ function PolicyListViewContent({
return (
<PageTemplate
title="Policies"
title="Documents"
actions={
<div className="flex items-center gap-2">
<Button onClick={handleOpenModal}>
<Plus className="mr-2 h-4 w-4" />
New policy
New document
</Button>
<Button onClick={() => setConfirmSigningModalOpen(true)}>
Send signing notifications
@@ -593,14 +593,14 @@ function PolicyListViewContent({
</div>
}
>
{/* Policy table */}
{/* Document table */}
<div className="rounded-lg border border-solid-b overflow-hidden bg-level-1">
<table className="w-full">
<thead>
<tr className="bg-level-1 text-left">
<th className="py-3 px-6 text-xs font-medium text-tertiary border-b border-low-b">
<div className="flex items-center gap-1">
Policy
Document
<ChevronDown className="h-3 w-3 text-quaternary" />
</div>
</th>
@@ -632,11 +632,11 @@ function PolicyListViewContent({
</tr>
</thead>
<tbody className="bg-level-1">
{policies.length > 0 ? (
policies.map((policy: any) => (
<PolicyTableRow
key={policy.id}
policy={policy}
{documents.length > 0 ? (
documents.map((document: any) => (
<DocumentTableRow
key={document.id}
document={document}
organizationId={organizationId!}
/>
))
@@ -644,13 +644,13 @@ function PolicyListViewContent({
<tr>
<td colSpan={6} className="py-12 text-center">
<div className="flex flex-col items-center gap-2">
<h3 className="text-lg font-medium">No policies found</h3>
<h3 className="text-lg font-medium">No documents found</h3>
<p className="text-tertiary">
Create your first policy to get started
Create your first document to get started
</p>
<Button onClick={handleOpenModal}>
<Plus className="mr-2 h-4 w-4" />
Create policy
Create document
</Button>
</div>
</td>
@@ -660,7 +660,7 @@ function PolicyListViewContent({
</table>
</div>
<CreatePolicyModal
<CreateDocumentModal
open={createModalOpen}
onOpenChange={handleCloseModal}
organizationId={organizationId!}
@@ -676,7 +676,7 @@ function PolicyListViewContent({
<DialogTitle>Send Signing Notifications</DialogTitle>
<DialogDescription>
This will send signing notifications to all users who have pending
policies to sign. Are you sure you want to continue?
documents to sign. Are you sure you want to continue?
</DialogDescription>
<DialogFooter className="gap-2 mt-4">
<Button
@@ -698,9 +698,9 @@ function PolicyListViewContent({
);
}
export default function PolicyListView() {
export default function DocumentListView() {
const [queryRef, loadQuery] =
useQueryLoader<PolicyListViewQuery>(policyListViewQuery);
useQueryLoader<DocumentListViewQuery>(documentListViewQuery);
const { organizationId } = useParams();
@@ -709,12 +709,12 @@ export default function PolicyListView() {
}, [loadQuery, organizationId]);
if (!queryRef) {
return <PolicyListViewSkeleton />;
return <DocumentListViewSkeleton />;
}
return (
<Suspense fallback={<PolicyListViewSkeleton />}>
{<PolicyListViewContent queryRef={queryRef} />}
<Suspense fallback={<DocumentListViewSkeleton />}>
{<DocumentListViewContent queryRef={queryRef} />}
</Suspense>
);
}

View File

@@ -4,26 +4,26 @@ import { lazy } from "@probo/react-lazy";
import { useLocation } from "react-router";
import { ErrorBoundaryWithLocation } from "../ErrorBoundary";
const EditPolicyView = lazy(() => import("./EditPolicyView"));
const EditDocumentView = lazy(() => import("./EditDocumentView"));
export function EditPolicyViewSkeleton() {
export function EditDocumentViewSkeleton() {
return (
<PageTemplateSkeleton
title="Edit Policy"
description="Edit an existing policy"
title="Edit Document"
description="Edit an existing document"
>
<div className="bg-subtle-bg animate-pulse rounded-lg h-[600px]" />
</PageTemplateSkeleton>
);
}
export function EditPolicyPage() {
export function EditDocumentPage() {
const location = useLocation();
return (
<Suspense key={location.pathname} fallback={<EditPolicyViewSkeleton />}>
<Suspense key={location.pathname} fallback={<EditDocumentViewSkeleton />}>
<ErrorBoundaryWithLocation>
<EditPolicyView />
<EditDocumentView />
</ErrorBoundaryWithLocation>
</Suspense>
);

View File

@@ -13,27 +13,27 @@ import { Textarea } from "@/components/ui/textarea";
import { Label } from "@/components/ui/label";
import { toast } from "@/hooks/use-toast";
import { Suspense } from "react";
import type { EditPolicyViewQuery } from "./__generated__/EditPolicyViewQuery.graphql";
import type { EditPolicyViewMutation as EditPolicyViewMutationType } from "./__generated__/EditPolicyViewMutation.graphql";
import type { EditDocumentViewQuery } from "./__generated__/EditDocumentViewQuery.graphql";
import type { EditDocumentViewMutation as EditDocumentViewMutationType } from "./__generated__/EditDocumentViewMutation.graphql";
import { PageTemplate } from "@/components/PageTemplate";
import { EditPolicyViewSkeleton } from "./EditPolicyPage";
import { EditDocumentViewSkeleton } from "./EditDocumentPage";
const editPolicyViewQuery = graphql`
query EditPolicyViewQuery(
$policyId: ID!
const editDocumentViewQuery = graphql`
query EditDocumentViewQuery(
$documentId: ID!
$organizationId: ID!
$policyVersionId: ID!
$documentVersionId: ID!
) {
policyVersion: node(id: $policyVersionId) {
documentVersion: node(id: $documentVersionId) {
id
... on PolicyVersion {
... on DocumentVersion {
content
}
}
policy: node(id: $policyId) {
document: node(id: $documentId) {
id
... on Policy {
... on Document {
title
owner {
id
@@ -47,10 +47,10 @@ const editPolicyViewQuery = graphql`
}
`;
const UpdatePolicyMutation = graphql`
mutation EditPolicyViewMutation($input: UpdatePolicyVersionInput!) {
updatePolicyVersion(input: $input) {
policyVersion {
const UpdateDocumentMutation = graphql`
mutation EditDocumentViewMutation($input: UpdateDocumentVersionInput!) {
updateDocumentVersion(input: $input) {
documentVersion {
id
content
}
@@ -58,75 +58,75 @@ const UpdatePolicyMutation = graphql`
}
`;
function EditPolicyViewContent({
function EditDocumentViewContent({
queryRef,
}: {
queryRef: PreloadedQuery<EditPolicyViewQuery>;
queryRef: PreloadedQuery<EditDocumentViewQuery>;
}) {
const navigate = useNavigate();
const { organizationId, policyId, versionId } = useParams();
const data = usePreloadedQuery<EditPolicyViewQuery>(
editPolicyViewQuery,
const { organizationId, documentId, versionId } = useParams();
const data = usePreloadedQuery<EditDocumentViewQuery>(
editDocumentViewQuery,
queryRef,
);
const [content, setContent] = useState(data.policyVersion.content || "");
const [content, setContent] = useState(data.documentVersion.content || "");
const [updatePolicy, isSubmitting] =
useMutation<EditPolicyViewMutationType>(UpdatePolicyMutation);
const [updateDocument, isSubmitting] =
useMutation<EditDocumentViewMutationType>(UpdateDocumentMutation);
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
updatePolicy({
updateDocument({
variables: {
input: {
policyVersionId: data.policyVersion.id,
documentVersionId: data.documentVersion.id,
content,
},
},
onCompleted: (_, errors) => {
if (errors) {
console.error("Error updating policy:", errors);
console.error("Error updating document:", errors);
toast({
title: "Error",
description: "Failed to update policy. Please try again.",
description: "Failed to update document. Please try again.",
});
return;
}
toast({
title: "Success",
description: "Policy updated successfully.",
description: "Document updated successfully.",
});
navigate(`/organizations/${organizationId}/policies/${policyId}`);
navigate(`/organizations/${organizationId}/documents/${documentId}`);
},
onError: (error) => {
console.error("Error updating policy:", error);
console.error("Error updating document:", error);
toast({
title: "Error",
description: "Failed to update policy. Please try again.",
description: "Failed to update document. Please try again.",
});
},
});
};
return (
<PageTemplate title="Update Policy" description="Update an existing policy">
<PageTemplate title="Update Document" description="Update an existing document">
<form onSubmit={handleSubmit}>
<div className="grid gap-6">
<Card>
<CardHeader>
<CardTitle>Policy Information</CardTitle>
<CardTitle>Document Information</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-2">
<Label htmlFor="content">Policy Content</Label>
<Label htmlFor="content">Document Content</Label>
<div className="min-h-[500px]">
<Textarea
id="content"
placeholder="Enter policy description"
placeholder="Enter document description"
value={content}
onChange={(e) => setContent(e.target.value)}
required
@@ -144,14 +144,14 @@ function EditPolicyViewContent({
variant="outline"
onClick={() =>
navigate(
`/organizations/${organizationId}/policies/${policyId}`,
`/organizations/${organizationId}/documents/${documentId}`,
)
}
>
Cancel
</Button>
<Button type="submit" disabled={isSubmitting}>
{isSubmitting ? "Updating..." : "Update Policy"}
{isSubmitting ? "Updating..." : "Update Document"}
</Button>
</div>
</div>
@@ -160,26 +160,26 @@ function EditPolicyViewContent({
);
}
export default function EditPolicyView() {
const { organizationId, policyId, versionId } = useParams();
export default function EditDocumentView() {
const { organizationId, documentId, versionId } = useParams();
const [queryRef, loadQuery] =
useQueryLoader<EditPolicyViewQuery>(editPolicyViewQuery);
useQueryLoader<EditDocumentViewQuery>(editDocumentViewQuery);
useEffect(() => {
loadQuery({
organizationId: organizationId!,
policyId: policyId!,
policyVersionId: versionId!,
documentId: documentId!,
documentVersionId: versionId!,
});
}, [organizationId, policyId, versionId]);
}, [organizationId, documentId, versionId]);
if (!queryRef) {
return <EditPolicyViewSkeleton />;
return <EditDocumentViewSkeleton />;
}
return (
<Suspense fallback={<EditPolicyViewSkeleton />}>
<EditPolicyViewContent queryRef={queryRef} />
<Suspense fallback={<EditDocumentViewSkeleton />}>
<EditDocumentViewContent queryRef={queryRef} />
</Suspense>
);
}

View File

@@ -5,9 +5,9 @@ import { useLocation } from "react-router";
import { ErrorBoundaryWithLocation } from "../ErrorBoundary";
import { lazy } from "@probo/react-lazy";
const ShowPolicyView = lazy(() => import("./ShowPolicyView"));
const ShowDocumentView = lazy(() => import("./ShowDocumentView"));
export function ShowPolicyViewSkeleton() {
export function ShowDocumentViewSkeleton() {
return (
<PageTemplateSkeleton
withDescription
@@ -108,13 +108,13 @@ export function ShowPolicyViewSkeleton() {
);
}
export function ShowPolicyPage() {
export function ShowDocumentPage() {
const location = useLocation();
return (
<Suspense key={location.pathname} fallback={<ShowPolicyViewSkeleton />}>
<Suspense key={location.pathname} fallback={<ShowDocumentViewSkeleton />}>
<ErrorBoundaryWithLocation>
<ShowPolicyView />
<ShowDocumentView />
</ErrorBoundaryWithLocation>
</Suspense>
);

View File

@@ -20,12 +20,12 @@ import {
import { Button } from "@/components/ui/button";
import { useToast } from "@/hooks/use-toast";
import { PageTemplate } from "@/components/PageTemplate";
import type { ShowPolicyViewQuery } from "./__generated__/ShowPolicyViewQuery.graphql";
import { ShowPolicyViewPublishMutation } from "./__generated__/ShowPolicyViewPublishMutation.graphql";
import { ShowPolicyViewCreateDraftMutation } from "./__generated__/ShowPolicyViewCreateDraftMutation.graphql";
import type { ShowDocumentViewQuery } from "./__generated__/ShowDocumentViewQuery.graphql";
import { ShowDocumentViewPublishMutation } from "./__generated__/ShowDocumentViewPublishMutation.graphql";
import { ShowDocumentViewCreateDraftMutation } from "./__generated__/ShowDocumentViewCreateDraftMutation.graphql";
import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
import { ShowPolicyViewSkeleton } from "./ShowPolicyPage";
import { ShowDocumentViewSkeleton } from "./ShowDocumentPage";
import { format } from "date-fns";
import {
DropdownMenu,
@@ -45,20 +45,20 @@ import { SignaturesModal } from "./SignaturesModal";
import { VersionHistoryModal } from "./VersionHistoryModal";
import rehypeRaw from "rehype-raw";
import { createRoot } from "react-dom/client";
import { policyVersionsFragment } from "./SignaturesModal";
import type { SignaturesModal_policyVersions$key } from "./__generated__/SignaturesModal_policyVersions.graphql";
import { documentVersionsFragment } from "./SignaturesModal";
import type { SignaturesModal_documentVersions$key } from "./__generated__/SignaturesModal_documentVersions.graphql";
const policyViewQuery = graphql`
query ShowPolicyViewQuery($policyId: ID!, $organizationId: ID!) {
const documentViewQuery = graphql`
query ShowDocumentViewQuery($documentId: ID!, $organizationId: ID!) {
organization: node(id: $organizationId) {
... on Organization {
name
}
}
node(id: $policyId) {
node(id: $documentId) {
id
... on Policy {
... on Document {
title
description
createdAt
@@ -70,8 +70,8 @@ const policyViewQuery = graphql`
primaryEmailAddress
}
...SignaturesModal_policyVersions
...VersionHistoryModal_policyVersions
...SignaturesModal_documentVersions
...VersionHistoryModal_documentVersions
latestVersion: versions(first: 1) {
edges {
@@ -95,14 +95,14 @@ const policyViewQuery = graphql`
}
`;
const publishPolicyVersionMutation = graphql`
mutation ShowPolicyViewPublishMutation($input: PublishPolicyVersionInput!) {
publishPolicyVersion(input: $input) {
policy {
const publishDocumentVersionMutation = graphql`
mutation ShowDocumentViewPublishMutation($input: PublishDocumentVersionInput!) {
publishDocumentVersion(input: $input) {
document {
id
currentPublishedVersion
}
policyVersion {
documentVersion {
id
status
publishedAt
@@ -114,12 +114,12 @@ const publishPolicyVersionMutation = graphql`
}
`;
const createDraftPolicyVersionMutation = graphql`
mutation ShowPolicyViewCreateDraftMutation(
$input: CreateDraftPolicyVersionInput!
const createDraftDocumentVersionMutation = graphql`
mutation ShowDocumentViewCreateDraftMutation(
$input: CreateDraftDocumentVersionInput!
) {
createDraftPolicyVersion(input: $input) {
policyVersionEdge {
createDraftDocumentVersion(input: $input) {
documentVersionEdge {
node {
id
version
@@ -130,22 +130,22 @@ const createDraftPolicyVersionMutation = graphql`
}
`;
function ShowPolicyContent({
function ShowDocumentContent({
queryRef,
}: {
queryRef: PreloadedQuery<ShowPolicyViewQuery>;
queryRef: PreloadedQuery<ShowDocumentViewQuery>;
}) {
const data = usePreloadedQuery<ShowPolicyViewQuery>(
policyViewQuery,
const data = usePreloadedQuery<ShowDocumentViewQuery>(
documentViewQuery,
queryRef,
);
const policy = data.node;
const documentValue = data.node;
const { organizationId } = useParams();
const navigate = useNavigate();
const { toast } = useToast();
const [queryRef2, loadQuery] =
useQueryLoader<ShowPolicyViewQuery>(policyViewQuery);
useQueryLoader<ShowDocumentViewQuery>(documentViewQuery);
const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false);
const [isDeleting, setIsDeleting] = useState(false);
const [isVersionHistoryOpen, setIsVersionHistoryOpen] = useState(false);
@@ -153,13 +153,13 @@ function ShowPolicyContent({
const printContentRef = useRef<HTMLDivElement>(null);
const [publishDraft, isPublishInFlight] =
useMutation<ShowPolicyViewPublishMutation>(publishPolicyVersionMutation);
useMutation<ShowDocumentViewPublishMutation>(publishDocumentVersionMutation);
const [createDraft, isCreateDraftInFlight] =
useMutation<ShowPolicyViewCreateDraftMutation>(
createDraftPolicyVersionMutation,
useMutation<ShowDocumentViewCreateDraftMutation>(
createDraftDocumentVersionMutation,
);
const latestVersionEdge = policy.latestVersion?.edges[0];
const latestVersionEdge = documentValue.latestVersion?.edges[0];
const latestVersionNode = latestVersionEdge?.node;
const isDraft = latestVersionNode?.status === "DRAFT";
@@ -168,40 +168,40 @@ function ShowPolicyContent({
// No need to update selectedVersion state since we're using the VersionHistoryModal component
}, []);
// Handle delete policy
const handleDeletePolicy = useCallback(() => {
// Handle delete document
const handleDeleteDocument = useCallback(() => {
setIsDeleteDialogOpen(true);
}, []);
// Confirm delete policy
const confirmDeletePolicy = useCallback(() => {
// Confirm delete document
const confirmDeleteDocument = useCallback(() => {
setIsDeleting(true);
setTimeout(() => {
toast({
title: "Policy deleted",
description: "The policy has been deleted successfully",
title: "Document deleted",
description: "The document has been deleted successfully",
});
setIsDeleting(false);
setIsDeleteDialogOpen(false);
navigate(`/organizations/${organizationId}/policies`);
navigate(`/organizations/${organizationId}/documents`);
}, 1000);
}, [toast, navigate, organizationId]);
// Navigate to publish flow
const handlePublish = useCallback(() => {
if (!policy.id) return;
if (!documentValue.id) return;
publishDraft({
variables: {
input: {
policyId: policy.id,
documentId: documentValue.id,
},
},
onCompleted: (_, errors) => {
if (errors) {
toast({
title: "Error publishing policy",
title: "Error publishing document",
description: errors[0]?.message || "An unknown error occurred",
variant: "destructive",
});
@@ -209,22 +209,22 @@ function ShowPolicyContent({
}
toast({
title: "Policy published",
description: `The policy has been published successfully`,
title: "Document published",
description: `The document has been published successfully`,
});
// Reload the query to refresh the data
loadQuery({ policyId: policy.id, organizationId: organizationId! });
loadQuery({ documentId: documentValue.id, organizationId: organizationId! });
},
onError: (error) => {
toast({
title: "Error publishing policy",
title: "Error publishing document",
description: error.message || "An unknown error occurred",
variant: "destructive",
});
},
});
}, [policy.id, publishDraft, toast, loadQuery]);
}, [documentValue.id, publishDraft, toast, loadQuery]);
// Open version history modal
const handleVersionHistoryClick = useCallback(() => {
@@ -241,23 +241,23 @@ function ShowPolicyContent({
});
setIsVersionHistoryOpen(false);
// Reload the query to refresh the data
if (policy.id) {
loadQuery({ policyId: policy.id, organizationId: organizationId! });
if (documentValue.id) {
loadQuery({ documentId: documentValue.id, organizationId: organizationId! });
}
},
[policy.id, loadQuery, toast],
[documentValue.id, loadQuery, toast],
);
// Handle edit policy
const handleEditPolicy = useCallback(() => {
if (!policy.id) return;
// Handle edit document
const handleEditDocument = useCallback(() => {
if (!documentValue.id) return;
if (latestVersionNode?.status === "PUBLISHED") {
// Create a new draft version first
createDraft({
variables: {
input: {
policyID: policy.id,
documentID: documentValue.id,
},
},
onCompleted: (response, errors) => {
@@ -271,9 +271,9 @@ function ShowPolicyContent({
}
const newDraftId =
response.createDraftPolicyVersion.policyVersionEdge.node.id;
response.createDraftDocumentVersion.documentVersionEdge.node.id;
navigate(
`/organizations/${organizationId}/policies/${policy.id}/versions/${newDraftId}/edit`,
`/organizations/${organizationId}/documents/${documentValue.id}/versions/${newDraftId}/edit`,
);
},
onError: (error) => {
@@ -287,11 +287,11 @@ function ShowPolicyContent({
} else {
// Navigate directly to edit if it's already a draft
navigate(
`/organizations/${organizationId}/policies/${policy.id}/versions/${latestVersionNode?.id}/edit`,
`/organizations/${organizationId}/documents/${documentValue.id}/versions/${latestVersionNode?.id}/edit`,
);
}
}, [
policy.id,
documentValue.id,
latestVersionNode,
createDraft,
navigate,
@@ -306,10 +306,10 @@ function ShowPolicyContent({
return format(date, "MMM d, yyyy");
};
// Get policy signatures data
const policyData = useFragment<SignaturesModal_policyVersions$key>(
policyVersionsFragment,
policy as unknown as SignaturesModal_policyVersions$key
// Get document signatures data
const documentData = useFragment<SignaturesModal_documentVersions$key>(
documentVersionsFragment,
documentValue as unknown as SignaturesModal_documentVersions$key
);
// Handle PDF download
@@ -324,23 +324,23 @@ function ShowPolicyContent({
});
return;
}
// Get version nodes and signatures from the fragment data we have
const versionNodes = policyData?.policyVersions?.edges?.map(edge => edge.node) || [];
const versionNodes = documentData?.documentVersions?.edges?.map(edge => edge.node) || [];
const currentVersion = versionNodes.find(v => v.version === latestVersionNode?.version);
const signatures = currentVersion?.signatures?.edges?.map(edge => edge.node) || [];
// Create temporary div to render and capture markdown
const tempDiv = document.createElement('div');
const tempDiv = window.document.createElement('div');
const root = createRoot(tempDiv);
// Render the markdown content to HTML
root.render(
<ReactMarkdown remarkPlugins={[remarkGfm]} rehypePlugins={[rehypeRaw]}>
{latestVersionNode?.content || 'No content available'}
</ReactMarkdown>
);
// Give React a moment to render the content
setTimeout(() => {
const renderedMarkdown = tempDiv.innerHTML;
@@ -369,13 +369,13 @@ function ShowPolicyContent({
</table>
`;
}
// Create the print document content with the policy document data
// Create the print document content with the document document data
printWindow.document.write(`
<!DOCTYPE html>
<html>
<head>
<title>${policy.title || 'Policy Document'}</title>
<title>${documentValue.title || 'Document Document'}</title>
<style>
body { font-family: system-ui, -apple-system, sans-serif; margin: 40px; }
.header { margin-bottom: 30px; }
@@ -386,7 +386,7 @@ function ShowPolicyContent({
.signature-title { margin-top: 20px; font-weight: bold; font-size: 1.2em; margin-bottom: 10px; }
h1 { font-size: 24px; margin-bottom: 20px; }
.company-name { font-size: 16px; color: #555; margin-bottom: 5px; }
/* Markdown styles */
.content h1 { font-size: 1.8em; margin-top: 1em; margin-bottom: 0.5em; }
.content h2 { font-size: 1.5em; margin-top: 1em; margin-bottom: 0.5em; }
@@ -403,7 +403,7 @@ function ShowPolicyContent({
.content table { border-collapse: collapse; width: 100%; margin-bottom: 1em; }
.content th, .content td { border: 1px solid #ccc; padding: 0.5em; }
.content th { background: #f4f4f4; }
@media print {
body { margin: 0; }
.page-break { page-break-after: always; }
@@ -426,12 +426,12 @@ function ShowPolicyContent({
</head>
<body>
<div class="header">
<h1>${policy.title || 'Policy Document'}</h1>
<h1>${documentValue.title || 'Document Document'}</h1>
<div class="metadata">
<div class="metadata-item"><strong>Version:</strong> ${latestVersionNode?.version || 'N/A'}</div>
<div class="metadata-item"><strong>Status:</strong> ${latestVersionNode?.status === 'PUBLISHED' ? 'Published' : 'Draft'}</div>
<div class="metadata-item"><strong>Published Date:</strong> ${latestVersionNode?.status === 'PUBLISHED' ? formatDate(latestVersionNode.publishedAt || '') : 'Not yet published'}</div>
<div class="metadata-item"><strong>Owner:</strong> ${policy.owner?.fullName || 'Unknown'}</div>
<div class="metadata-item"><strong>Owner:</strong> ${documentValue.owner?.fullName || 'Unknown'}</div>
<div class="metadata-item"><strong>Last Modified:</strong> ${formatDate(latestVersionNode?.updatedAt)}</div>
<div class="metadata-item"><strong>Company:</strong> ${data.organization.name}</div>
</div>
@@ -446,27 +446,27 @@ function ShowPolicyContent({
</body>
</html>
`);
// Trigger print
printWindow.document.close();
printWindow.focus();
// Use setTimeout to give the browser time to load any resources
setTimeout(() => {
printWindow.print();
// Optional: close the window after printing
// printWindow.close();
}, 500);
// Clean up
root.unmount();
}, 100);
}, [policy, latestVersionNode, formatDate, toast, policyData, data.organization.name!]);
}, [documentValue, latestVersionNode, formatDate, toast, documentData, data.organization.name!]);
return (
<PageTemplate
title={policy.title!}
title={documentValue.title!}
actions={
<div className="flex items-center gap-2">
<Button
@@ -523,19 +523,19 @@ function ShowPolicyContent({
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem asChild>
<div onClick={handleEditPolicy}>
<div onClick={handleEditDocument}>
<Edit className="mr-2 h-4 w-4" />
{latestVersionNode?.status === "PUBLISHED"
? "Create new draft"
: "Edit draft policy"}
: "Edit draft document"}
</div>
</DropdownMenuItem>
<DropdownMenuItem
onClick={handleDeletePolicy}
onClick={handleDeleteDocument}
className="text-danger focus:text-danger focus:bg-danger-bg"
>
<Trash2 className="mr-2 h-4 w-4" />
Delete policy
Delete document
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
@@ -548,7 +548,7 @@ function ShowPolicyContent({
<div className="bg-gray-50 rounded-lg border border-solid-b shadow-sm p-6 mb-4">
<div className="grid grid-cols-2 gap-y-3">
<div>
<span className="font-medium">Document Title:</span> {policy.title}
<span className="font-medium">Document Title:</span> {documentValue.title}
</div>
<div>
<span className="font-medium">Version:</span> {latestVersionNode.version || "N/A"}
@@ -560,7 +560,7 @@ function ShowPolicyContent({
<span className="font-medium">Published Date:</span> {latestVersionNode.status === "PUBLISHED" ? formatDate(latestVersionNode.publishedAt || "") : "Not yet published"}
</div>
<div>
<span className="font-medium">Owner:</span> {policy.owner?.fullName || "Unknown"}
<span className="font-medium">Owner:</span> {documentValue.owner?.fullName || "Unknown"}
</div>
<div>
<span className="font-medium">Last Modified:</span> {formatDate(latestVersionNode.updatedAt)}
@@ -590,7 +590,7 @@ function ShowPolicyContent({
<div>
{latestVersionNode.status === "PUBLISHED"
? `Published on ${formatDate(latestVersionNode.publishedAt || "")}${latestVersionNode.publishedBy ? ` by ${latestVersionNode.publishedBy.fullName}` : ""}`
: `Last modified on ${formatDate(latestVersionNode.updatedAt)} by ${policy.owner?.fullName || "Unknown"}`}
: `Last modified on ${formatDate(latestVersionNode.updatedAt)} by ${documentValue.owner?.fullName || "Unknown"}`}
</div>
</div>
</div>
@@ -606,15 +606,15 @@ function ShowPolicyContent({
<SignaturesModal
isOpen={isSignaturesModalOpen}
onClose={() => setIsSignaturesModalOpen(false)}
policyRef={policy}
owner={policy.owner}
documentRef={documentValue}
owner={documentValue.owner}
/>
{/* Version History Modal */}
<VersionHistoryModal
isOpen={isVersionHistoryOpen}
onClose={() => setIsVersionHistoryOpen(false)}
policyRef={policy}
documentRef={documentValue}
onRestoreVersion={handleRestoreVersion}
/>
@@ -622,9 +622,9 @@ function ShowPolicyContent({
<Dialog open={isDeleteDialogOpen} onOpenChange={setIsDeleteDialogOpen}>
<DialogContent>
<DialogHeader>
<DialogTitle>Delete Policy</DialogTitle>
<DialogTitle>Delete Document</DialogTitle>
<DialogDescription>
Are you sure you want to delete the policy "{policy.title}"? This
Are you sure you want to delete the document &ldquo;{documentValue.title}&rdquo;? This
action cannot be undone.
</DialogDescription>
</DialogHeader>
@@ -638,7 +638,7 @@ function ShowPolicyContent({
</Button>
<Button
variant="destructive"
onClick={confirmDeletePolicy}
onClick={confirmDeleteDocument}
disabled={isDeleting}
>
{isDeleting ? "Deleting..." : "Delete"}
@@ -650,22 +650,22 @@ function ShowPolicyContent({
);
}
export default function ShowPolicyView() {
export default function ShowDocumentView() {
const [queryRef, loadQuery] =
useQueryLoader<ShowPolicyViewQuery>(policyViewQuery);
const { policyId, organizationId } = useParams();
useQueryLoader<ShowDocumentViewQuery>(documentViewQuery);
const { documentId, organizationId } = useParams();
useEffect(() => {
loadQuery({ policyId: policyId!, organizationId: organizationId! });
}, [loadQuery, policyId, organizationId]);
loadQuery({ documentId: documentId!, organizationId: organizationId! });
}, [loadQuery, documentId, organizationId]);
if (!queryRef) {
return <ShowPolicyViewSkeleton />;
return <ShowDocumentViewSkeleton />;
}
return (
<Suspense fallback={<ShowPolicyViewSkeleton />}>
<ShowPolicyContent queryRef={queryRef} />
<Suspense fallback={<ShowDocumentViewSkeleton />}>
<ShowDocumentContent queryRef={queryRef} />
</Suspense>
);
}

View File

@@ -1,7 +1,7 @@
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { format } from "date-fns";
interface PolicyVersionSignature {
interface DocumentVersionSignature {
id: string;
state: "REQUESTED" | "SIGNED";
signedBy: {
@@ -15,7 +15,7 @@ interface PolicyVersionSignature {
}
interface SignaturesListProps {
signatures: PolicyVersionSignature[];
signatures: DocumentVersionSignature[];
}
export function SignaturesList({ signatures }: SignaturesListProps) {

View File

@@ -12,9 +12,9 @@ import {
ConnectionHandler,
} from "react-relay";
import type {
SignaturesModal_policyVersions$data,
SignaturesModal_policyVersions$key,
} from "./__generated__/SignaturesModal_policyVersions.graphql";
SignaturesModal_documentVersions$data,
SignaturesModal_documentVersions$key,
} from "./__generated__/SignaturesModal_documentVersions.graphql";
import type { SignaturesModalRequestSignatureMutation } from "./__generated__/SignaturesModalRequestSignatureMutation.graphql";
import type { SignaturesModalOrganizationQuery } from "./__generated__/SignaturesModalOrganizationQuery.graphql";
import { useToast } from "@/hooks/use-toast";
@@ -22,10 +22,10 @@ import { useParams } from "react-router";
import { Loader2, CheckCircle2, Clock } from "lucide-react";
import { PreloadedQuery } from "react-relay";
export const policyVersionsFragment = graphql`
fragment SignaturesModal_policyVersions on Policy {
export const documentVersionsFragment = graphql`
fragment SignaturesModal_documentVersions on Document {
title
policyVersions: versions(first: 10) {
documentVersions: versions(first: 10) {
edges {
node {
id
@@ -37,7 +37,7 @@ export const policyVersionsFragment = graphql`
fullName
}
signatures(first: 100)
@connection(key: "SignaturesModal_policyVersions_signatures") {
@connection(key: "SignaturesModal_documentVersions_signatures") {
edges {
node {
id
@@ -66,7 +66,7 @@ const requestSignatureMutation = graphql`
$connections: [ID!]!
) {
requestSignature(input: $input) {
policyVersionSignatureEdge @prependEdge(connections: $connections) {
documentVersionSignatureEdge @prependEdge(connections: $connections) {
node {
id
state
@@ -108,7 +108,7 @@ const organizationQuery = graphql`
interface SignaturesModalProps {
isOpen: boolean;
onClose: () => void;
policyRef: SignaturesModal_policyVersions$key;
documentRef: SignaturesModal_documentVersions$key;
owner?: {
fullName: string;
} | null;
@@ -117,16 +117,16 @@ interface SignaturesModalProps {
export function SignaturesModal({
isOpen,
onClose,
policyRef,
documentRef,
owner,
}: SignaturesModalProps) {
const data = useFragment<SignaturesModal_policyVersions$key>(
policyVersionsFragment,
policyRef,
const data = useFragment<SignaturesModal_documentVersions$key>(
documentVersionsFragment,
documentRef,
);
const versionNodes =
data?.policyVersions?.edges?.map((edge) => edge.node) || [];
data?.documentVersions?.edges?.map((edge) => edge.node) || [];
const publishedVersions = versionNodes
.filter((v) => v.status === "PUBLISHED")
.sort((a, b) => b.version - a.version);
@@ -177,13 +177,13 @@ export function SignaturesModal({
commitRequestSignature({
variables: {
input: {
policyVersionId: selectedVersionData.id,
documentVersionId: selectedVersionData.id,
signatoryId: personId,
},
connections: [
ConnectionHandler.getConnectionID(
selectedVersionData.id,
"SignaturesModal_policyVersions_signatures",
"SignaturesModal_documentVersions_signatures",
),
],
},
@@ -406,7 +406,7 @@ function PeopleAndSignaturesList({
onRequestSignature: (personId: string) => void;
requestingId: string | null;
existingSignatures: Array<
SignaturesModal_policyVersions$data["policyVersions"]["edges"][0]["node"]["signatures"]["edges"][0]["node"]
SignaturesModal_documentVersions$data["documentVersions"]["edges"][0]["node"]["signatures"]["edges"][0]["node"]
>;
formatDateTime: (date?: string | null) => string;
}) {

View File

@@ -5,12 +5,12 @@ import { Button } from "@/components/ui/button";
import { useState } from "react";
import ReactMarkdown from "react-markdown";
import { graphql, useFragment } from "react-relay";
import type { VersionHistoryModal_policyVersions$key } from "./__generated__/VersionHistoryModal_policyVersions.graphql";
import type { VersionHistoryModal_documentVersions$key } from "./__generated__/VersionHistoryModal_documentVersions.graphql";
import remarkGfm from "remark-gfm";
import rehypeRaw from "rehype-raw";
export const policyVersionsFragment = graphql`
fragment VersionHistoryModal_policyVersions on Policy {
export const documentVersionsFragment = graphql`
fragment VersionHistoryModal_documentVersions on Document {
title
owner {
fullName
@@ -37,19 +37,19 @@ export const policyVersionsFragment = graphql`
interface VersionHistoryModalProps {
isOpen: boolean;
onClose: () => void;
policyRef: VersionHistoryModal_policyVersions$key;
documentRef: VersionHistoryModal_documentVersions$key;
onRestoreVersion?: (versionNumber: number) => void;
}
export function VersionHistoryModal({
isOpen,
onClose,
policyRef,
documentRef,
onRestoreVersion,
}: VersionHistoryModalProps) {
const data = useFragment<VersionHistoryModal_policyVersions$key>(
policyVersionsFragment,
policyRef,
const data = useFragment<VersionHistoryModal_documentVersions$key>(
documentVersionsFragment,
documentRef,
);
// Safely access and extract version nodes

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<885dc2a54ba3b8d4663e197cfe61d704>>
* @generated SignedSource<<095491df1cce828cc3ec9ba9f2ad4183>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -9,19 +9,19 @@
// @ts-nocheck
import { ConcreteRequest } from 'relay-runtime';
export type CreatePolicyInput = {
export type CreateDocumentInput = {
content: string;
organizationId: string;
ownerId: string;
title: string;
};
export type PolicyListViewCreateMutation$variables = {
export type DocumentListViewCreateMutation$variables = {
connections: ReadonlyArray<string>;
input: CreatePolicyInput;
input: CreateDocumentInput;
};
export type PolicyListViewCreateMutation$data = {
readonly createPolicy: {
readonly policyEdge: {
export type DocumentListViewCreateMutation$data = {
readonly createDocument: {
readonly documentEdge: {
readonly node: {
readonly createdAt: string;
readonly description: string;
@@ -36,9 +36,9 @@ export type PolicyListViewCreateMutation$data = {
};
};
};
export type PolicyListViewCreateMutation = {
response: PolicyListViewCreateMutation$data;
variables: PolicyListViewCreateMutation$variables;
export type DocumentListViewCreateMutation = {
response: DocumentListViewCreateMutation$data;
variables: DocumentListViewCreateMutation$variables;
};
const node: ConcreteRequest = (function(){
@@ -69,15 +69,15 @@ v3 = {
v4 = {
"alias": null,
"args": null,
"concreteType": "PolicyEdge",
"concreteType": "DocumentEdge",
"kind": "LinkedField",
"name": "policyEdge",
"name": "documentEdge",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "Policy",
"concreteType": "Document",
"kind": "LinkedField",
"name": "node",
"plural": false,
@@ -144,14 +144,14 @@ return {
],
"kind": "Fragment",
"metadata": null,
"name": "PolicyListViewCreateMutation",
"name": "DocumentListViewCreateMutation",
"selections": [
{
"alias": null,
"args": (v2/*: any*/),
"concreteType": "CreatePolicyPayload",
"concreteType": "CreateDocumentPayload",
"kind": "LinkedField",
"name": "createPolicy",
"name": "createDocument",
"plural": false,
"selections": [
(v4/*: any*/)
@@ -169,14 +169,14 @@ return {
(v0/*: any*/)
],
"kind": "Operation",
"name": "PolicyListViewCreateMutation",
"name": "DocumentListViewCreateMutation",
"selections": [
{
"alias": null,
"args": (v2/*: any*/),
"concreteType": "CreatePolicyPayload",
"concreteType": "CreateDocumentPayload",
"kind": "LinkedField",
"name": "createPolicy",
"name": "createDocument",
"plural": false,
"selections": [
(v4/*: any*/),
@@ -187,7 +187,7 @@ return {
"handle": "prependEdge",
"key": "",
"kind": "LinkedHandle",
"name": "policyEdge",
"name": "documentEdge",
"handleArgs": [
{
"kind": "Variable",
@@ -202,16 +202,16 @@ return {
]
},
"params": {
"cacheID": "f7aae6d7a0d276e71caec643e43b5ee5",
"cacheID": "006fad3aafaae7d03f5d85cd78ea39fb",
"id": null,
"metadata": {},
"name": "PolicyListViewCreateMutation",
"name": "DocumentListViewCreateMutation",
"operationKind": "mutation",
"text": "mutation PolicyListViewCreateMutation(\n $input: CreatePolicyInput!\n) {\n createPolicy(input: $input) {\n policyEdge {\n node {\n id\n title\n description\n createdAt\n updatedAt\n owner {\n id\n fullName\n }\n }\n }\n }\n}\n"
"text": "mutation DocumentListViewCreateMutation(\n $input: CreateDocumentInput!\n) {\n createDocument(input: $input) {\n documentEdge {\n node {\n id\n title\n description\n createdAt\n updatedAt\n owner {\n id\n fullName\n }\n }\n }\n }\n}\n"
}
};
})();
(node as any).hash = "d55ac010a7ad352d830d3a38f2f8d5a8";
(node as any).hash = "0b6f4540b8bc9dc8168ba33ed6340311";
export default node;

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<c71120e2c59cabbcb2f9e55dacd2be6c>>
* @generated SignedSource<<f20fbf3a0fd45a8f408e5fd364c9dfee>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -9,21 +9,21 @@
// @ts-nocheck
import { ConcreteRequest } from 'relay-runtime';
export type DeletePolicyInput = {
policyId: string;
export type DeleteDocumentInput = {
documentId: string;
};
export type PolicyListViewDeleteMutation$variables = {
export type DocumentListViewDeleteMutation$variables = {
connections: ReadonlyArray<string>;
input: DeletePolicyInput;
input: DeleteDocumentInput;
};
export type PolicyListViewDeleteMutation$data = {
readonly deletePolicy: {
readonly deletedPolicyId: string;
export type DocumentListViewDeleteMutation$data = {
readonly deleteDocument: {
readonly deletedDocumentId: string;
};
};
export type PolicyListViewDeleteMutation = {
response: PolicyListViewDeleteMutation$data;
variables: PolicyListViewDeleteMutation$variables;
export type DocumentListViewDeleteMutation = {
response: DocumentListViewDeleteMutation$data;
variables: DocumentListViewDeleteMutation$variables;
};
const node: ConcreteRequest = (function(){
@@ -48,7 +48,7 @@ v3 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "deletedPolicyId",
"name": "deletedDocumentId",
"storageKey": null
};
return {
@@ -59,14 +59,14 @@ return {
],
"kind": "Fragment",
"metadata": null,
"name": "PolicyListViewDeleteMutation",
"name": "DocumentListViewDeleteMutation",
"selections": [
{
"alias": null,
"args": (v2/*: any*/),
"concreteType": "DeletePolicyPayload",
"concreteType": "DeleteDocumentPayload",
"kind": "LinkedField",
"name": "deletePolicy",
"name": "deleteDocument",
"plural": false,
"selections": [
(v3/*: any*/)
@@ -84,14 +84,14 @@ return {
(v0/*: any*/)
],
"kind": "Operation",
"name": "PolicyListViewDeleteMutation",
"name": "DocumentListViewDeleteMutation",
"selections": [
{
"alias": null,
"args": (v2/*: any*/),
"concreteType": "DeletePolicyPayload",
"concreteType": "DeleteDocumentPayload",
"kind": "LinkedField",
"name": "deletePolicy",
"name": "deleteDocument",
"plural": false,
"selections": [
(v3/*: any*/),
@@ -102,7 +102,7 @@ return {
"handle": "deleteEdge",
"key": "",
"kind": "ScalarHandle",
"name": "deletedPolicyId",
"name": "deletedDocumentId",
"handleArgs": [
{
"kind": "Variable",
@@ -117,16 +117,16 @@ return {
]
},
"params": {
"cacheID": "3a182fce4a0616599bea274ddf7b95a1",
"cacheID": "61bee9d714b5ee08e1653d0ad426c6f5",
"id": null,
"metadata": {},
"name": "PolicyListViewDeleteMutation",
"name": "DocumentListViewDeleteMutation",
"operationKind": "mutation",
"text": "mutation PolicyListViewDeleteMutation(\n $input: DeletePolicyInput!\n) {\n deletePolicy(input: $input) {\n deletedPolicyId\n }\n}\n"
"text": "mutation DocumentListViewDeleteMutation(\n $input: DeleteDocumentInput!\n) {\n deleteDocument(input: $input) {\n deletedDocumentId\n }\n}\n"
}
};
})();
(node as any).hash = "d2a47c3563b6dcbbb88e78b954345768";
(node as any).hash = "f0da5a5c332d17752d23c2fbfcd0e503";
export default node;

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<fbe65b0fb14443a15a43bd3d4100118f>>
* @generated SignedSource<<4ae1fa075cd3c3fd2ae9d63e3ec0ae02>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -10,14 +10,14 @@
import { ConcreteRequest } from 'relay-runtime';
import { FragmentRefs } from "relay-runtime";
export type PolicyStatus = "DRAFT" | "PUBLISHED";
export type PolicyVersionSignatureState = "REQUESTED" | "SIGNED";
export type PolicyListViewQuery$variables = {
export type DocumentStatus = "DRAFT" | "PUBLISHED";
export type DocumentVersionSignatureState = "REQUESTED" | "SIGNED";
export type DocumentListViewQuery$variables = {
organizationId: string;
};
export type PolicyListViewQuery$data = {
export type DocumentListViewQuery$data = {
readonly organization: {
readonly policies?: {
readonly documents?: {
readonly edges: ReadonlyArray<{
readonly node: {
readonly createdAt: string;
@@ -38,11 +38,11 @@ export type PolicyListViewQuery$data = {
readonly edges: ReadonlyArray<{
readonly node: {
readonly id: string;
readonly state: PolicyVersionSignatureState;
readonly state: DocumentVersionSignatureState;
};
}>;
};
readonly status: PolicyStatus;
readonly status: DocumentStatus;
readonly updatedAt: string;
};
}>;
@@ -58,9 +58,9 @@ export type PolicyListViewQuery$data = {
};
};
};
export type PolicyListViewQuery = {
response: PolicyListViewQuery$data;
variables: PolicyListViewQuery$variables;
export type DocumentListViewQuery = {
response: DocumentListViewQuery$data;
variables: DocumentListViewQuery$variables;
};
const node: ConcreteRequest = (function(){
@@ -167,7 +167,7 @@ v11 = [
{
"alias": null,
"args": null,
"concreteType": "PolicyEdge",
"concreteType": "DocumentEdge",
"kind": "LinkedField",
"name": "edges",
"plural": true,
@@ -175,7 +175,7 @@ v11 = [
{
"alias": null,
"args": null,
"concreteType": "Policy",
"concreteType": "Document",
"kind": "LinkedField",
"name": "node",
"plural": false,
@@ -232,7 +232,7 @@ v11 = [
"value": 1
}
],
"concreteType": "PolicyVersionConnection",
"concreteType": "DocumentVersionConnection",
"kind": "LinkedField",
"name": "versions",
"plural": false,
@@ -240,7 +240,7 @@ v11 = [
{
"alias": null,
"args": null,
"concreteType": "PolicyVersionEdge",
"concreteType": "DocumentVersionEdge",
"kind": "LinkedField",
"name": "edges",
"plural": true,
@@ -248,7 +248,7 @@ v11 = [
{
"alias": null,
"args": null,
"concreteType": "PolicyVersion",
"concreteType": "DocumentVersion",
"kind": "LinkedField",
"name": "node",
"plural": false,
@@ -267,7 +267,7 @@ v11 = [
"args": [
(v7/*: any*/)
],
"concreteType": "PolicyVersionSignatureConnection",
"concreteType": "DocumentVersionSignatureConnection",
"kind": "LinkedField",
"name": "signatures",
"plural": false,
@@ -275,7 +275,7 @@ v11 = [
{
"alias": null,
"args": null,
"concreteType": "PolicyVersionSignatureEdge",
"concreteType": "DocumentVersionSignatureEdge",
"kind": "LinkedField",
"name": "edges",
"plural": true,
@@ -283,7 +283,7 @@ v11 = [
{
"alias": null,
"args": null,
"concreteType": "PolicyVersionSignature",
"concreteType": "DocumentVersionSignature",
"kind": "LinkedField",
"name": "node",
"plural": false,
@@ -351,7 +351,7 @@ return {
"argumentDefinitions": (v0/*: any*/),
"kind": "Fragment",
"metadata": null,
"name": "PolicyListViewQuery",
"name": "DocumentListViewQuery",
"selections": [
{
"alias": null,
@@ -382,16 +382,16 @@ return {
"name": "PeopleSelector_organization"
},
{
"alias": "policies",
"alias": "documents",
"args": [
(v4/*: any*/)
],
"concreteType": "PolicyConnection",
"concreteType": "DocumentConnection",
"kind": "LinkedField",
"name": "__PolicyListView_policies_connection",
"name": "__DocumentListView_documents_connection",
"plural": false,
"selections": (v11/*: any*/),
"storageKey": "__PolicyListView_policies_connection(orderBy:{\"direction\":\"ASC\",\"field\":\"TITLE\"})"
"storageKey": "__DocumentListView_documents_connection(orderBy:{\"direction\":\"ASC\",\"field\":\"TITLE\"})"
}
],
"type": "Organization",
@@ -408,7 +408,7 @@ return {
"operation": {
"argumentDefinitions": (v0/*: any*/),
"kind": "Operation",
"name": "PolicyListViewQuery",
"name": "DocumentListViewQuery",
"selections": [
{
"alias": null,
@@ -493,21 +493,21 @@ return {
{
"alias": null,
"args": (v14/*: any*/),
"concreteType": "PolicyConnection",
"concreteType": "DocumentConnection",
"kind": "LinkedField",
"name": "policies",
"name": "documents",
"plural": false,
"selections": (v11/*: any*/),
"storageKey": "policies(first:50,orderBy:{\"direction\":\"ASC\",\"field\":\"TITLE\"})"
"storageKey": "documents(first:50,orderBy:{\"direction\":\"ASC\",\"field\":\"TITLE\"})"
},
{
"alias": null,
"args": (v14/*: any*/),
"filters": (v13/*: any*/),
"handle": "connection",
"key": "PolicyListView_policies",
"key": "DocumentListView_documents",
"kind": "LinkedHandle",
"name": "policies"
"name": "documents"
}
],
"type": "Organization",
@@ -519,7 +519,7 @@ return {
]
},
"params": {
"cacheID": "1a9f6afc7b288a1051294afe154f14c4",
"cacheID": "6b91a4a77bedb9f66abeaeec8c1c5535",
"id": null,
"metadata": {
"connection": [
@@ -529,18 +529,18 @@ return {
"direction": "forward",
"path": [
"organization",
"policies"
"documents"
]
}
]
},
"name": "PolicyListViewQuery",
"name": "DocumentListViewQuery",
"operationKind": "query",
"text": "query PolicyListViewQuery(\n $organizationId: ID!\n) {\n viewer {\n user {\n id\n }\n id\n }\n organization: node(id: $organizationId) {\n __typename\n ... on Organization {\n ...PeopleSelector_organization\n policies(first: 50, orderBy: {field: TITLE, direction: ASC}) {\n edges {\n node {\n id\n title\n description\n currentPublishedVersion\n createdAt\n updatedAt\n owner {\n id\n fullName\n }\n versions(first: 1) {\n edges {\n node {\n id\n status\n updatedAt\n signatures(first: 100) {\n edges {\n node {\n id\n state\n }\n }\n }\n }\n }\n }\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n }\n id\n }\n}\n\nfragment PeopleSelector_organization on Organization {\n id\n peoples(first: 100, orderBy: {direction: ASC, field: FULL_NAME}) {\n edges {\n node {\n id\n fullName\n primaryEmailAddress\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n}\n"
"text": "query DocumentListViewQuery(\n $organizationId: ID!\n) {\n viewer {\n user {\n id\n }\n id\n }\n organization: node(id: $organizationId) {\n __typename\n ... on Organization {\n ...PeopleSelector_organization\n documents(first: 50, orderBy: {field: TITLE, direction: ASC}) {\n edges {\n node {\n id\n title\n description\n currentPublishedVersion\n createdAt\n updatedAt\n owner {\n id\n fullName\n }\n versions(first: 1) {\n edges {\n node {\n id\n status\n updatedAt\n signatures(first: 100) {\n edges {\n node {\n id\n state\n }\n }\n }\n }\n }\n }\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n }\n id\n }\n}\n\nfragment PeopleSelector_organization on Organization {\n id\n peoples(first: 100, orderBy: {direction: ASC, field: FULL_NAME}) {\n edges {\n node {\n id\n fullName\n primaryEmailAddress\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n}\n"
}
};
})();
(node as any).hash = "0aa2374fc681f6d130fbf03586707d5a";
(node as any).hash = "df33668bcdfe69a122a8846bb1d66df1";
export default node;

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<ec7d0ef29b8366117ddf75e75669f5fa>>
* @generated SignedSource<<25fe683751dd38b0c417c7df9a7f773f>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -12,17 +12,17 @@ import { ConcreteRequest } from 'relay-runtime';
export type SendSigningNotificationsInput = {
organizationId: string;
};
export type PolicyListViewSendSigningNotificationsMutation$variables = {
export type DocumentListViewSendSigningNotificationsMutation$variables = {
input: SendSigningNotificationsInput;
};
export type PolicyListViewSendSigningNotificationsMutation$data = {
export type DocumentListViewSendSigningNotificationsMutation$data = {
readonly sendSigningNotifications: {
readonly success: boolean;
};
};
export type PolicyListViewSendSigningNotificationsMutation = {
response: PolicyListViewSendSigningNotificationsMutation$data;
variables: PolicyListViewSendSigningNotificationsMutation$variables;
export type DocumentListViewSendSigningNotificationsMutation = {
response: DocumentListViewSendSigningNotificationsMutation$data;
variables: DocumentListViewSendSigningNotificationsMutation$variables;
};
const node: ConcreteRequest = (function(){
@@ -64,7 +64,7 @@ return {
"argumentDefinitions": (v0/*: any*/),
"kind": "Fragment",
"metadata": null,
"name": "PolicyListViewSendSigningNotificationsMutation",
"name": "DocumentListViewSendSigningNotificationsMutation",
"selections": (v1/*: any*/),
"type": "Mutation",
"abstractKey": null
@@ -73,20 +73,20 @@ return {
"operation": {
"argumentDefinitions": (v0/*: any*/),
"kind": "Operation",
"name": "PolicyListViewSendSigningNotificationsMutation",
"name": "DocumentListViewSendSigningNotificationsMutation",
"selections": (v1/*: any*/)
},
"params": {
"cacheID": "a6b25b9e4abf4243131b90b47f4e2ef4",
"cacheID": "0eb5a217a3b01f80c0ec446df15b0c40",
"id": null,
"metadata": {},
"name": "PolicyListViewSendSigningNotificationsMutation",
"name": "DocumentListViewSendSigningNotificationsMutation",
"operationKind": "mutation",
"text": "mutation PolicyListViewSendSigningNotificationsMutation(\n $input: SendSigningNotificationsInput!\n) {\n sendSigningNotifications(input: $input) {\n success\n }\n}\n"
"text": "mutation DocumentListViewSendSigningNotificationsMutation(\n $input: SendSigningNotificationsInput!\n) {\n sendSigningNotifications(input: $input) {\n success\n }\n}\n"
}
};
})();
(node as any).hash = "71b0a1e7d209ed71a9bb6552eaf95cee";
(node as any).hash = "8a959080f5aa7cc752ce039bd7383dfa";
export default node;

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<d8d76cb73e37916ddc34017550ff3702>>
* @generated SignedSource<<544173fd0ab2731cb9d3a976212ceca5>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -9,24 +9,24 @@
// @ts-nocheck
import { ConcreteRequest } from 'relay-runtime';
export type UpdatePolicyVersionInput = {
export type UpdateDocumentVersionInput = {
content: string;
policyVersionId: string;
documentVersionId: string;
};
export type EditPolicyViewMutation$variables = {
input: UpdatePolicyVersionInput;
export type EditDocumentViewMutation$variables = {
input: UpdateDocumentVersionInput;
};
export type EditPolicyViewMutation$data = {
readonly updatePolicyVersion: {
readonly policyVersion: {
export type EditDocumentViewMutation$data = {
readonly updateDocumentVersion: {
readonly documentVersion: {
readonly content: string;
readonly id: string;
};
};
};
export type EditPolicyViewMutation = {
response: EditPolicyViewMutation$data;
variables: EditPolicyViewMutation$variables;
export type EditDocumentViewMutation = {
response: EditDocumentViewMutation$data;
variables: EditDocumentViewMutation$variables;
};
const node: ConcreteRequest = (function(){
@@ -47,17 +47,17 @@ v1 = [
"variableName": "input"
}
],
"concreteType": "UpdatePolicyVersionPayload",
"concreteType": "UpdateDocumentVersionPayload",
"kind": "LinkedField",
"name": "updatePolicyVersion",
"name": "updateDocumentVersion",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "PolicyVersion",
"concreteType": "DocumentVersion",
"kind": "LinkedField",
"name": "policyVersion",
"name": "documentVersion",
"plural": false,
"selections": [
{
@@ -86,7 +86,7 @@ return {
"argumentDefinitions": (v0/*: any*/),
"kind": "Fragment",
"metadata": null,
"name": "EditPolicyViewMutation",
"name": "EditDocumentViewMutation",
"selections": (v1/*: any*/),
"type": "Mutation",
"abstractKey": null
@@ -95,20 +95,20 @@ return {
"operation": {
"argumentDefinitions": (v0/*: any*/),
"kind": "Operation",
"name": "EditPolicyViewMutation",
"name": "EditDocumentViewMutation",
"selections": (v1/*: any*/)
},
"params": {
"cacheID": "02b56043d606c6237dd502a7695fea50",
"cacheID": "92e3e1e6d3bf1a2066b2e71c437c3b4e",
"id": null,
"metadata": {},
"name": "EditPolicyViewMutation",
"name": "EditDocumentViewMutation",
"operationKind": "mutation",
"text": "mutation EditPolicyViewMutation(\n $input: UpdatePolicyVersionInput!\n) {\n updatePolicyVersion(input: $input) {\n policyVersion {\n id\n content\n }\n }\n}\n"
"text": "mutation EditDocumentViewMutation(\n $input: UpdateDocumentVersionInput!\n) {\n updateDocumentVersion(input: $input) {\n documentVersion {\n id\n content\n }\n }\n}\n"
}
};
})();
(node as any).hash = "556fbcc4fadc0011c1a2154dcffeb389";
(node as any).hash = "14fc2fdfcdb2ccb827e237d5fc1f0a6f";
export default node;

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<7738a95cc83ee25c08f6ddc556f144c1>>
* @generated SignedSource<<d1a5426a3bb5920a1d2f94e4f689c93f>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -10,16 +10,13 @@
import { ConcreteRequest } from 'relay-runtime';
import { FragmentRefs } from "relay-runtime";
export type EditPolicyViewQuery$variables = {
export type EditDocumentViewQuery$variables = {
documentId: string;
documentVersionId: string;
organizationId: string;
policyId: string;
policyVersionId: string;
};
export type EditPolicyViewQuery$data = {
readonly organization: {
readonly " $fragmentSpreads": FragmentRefs<"PeopleSelector_organization">;
};
readonly policy: {
export type EditDocumentViewQuery$data = {
readonly document: {
readonly id: string;
readonly owner?: {
readonly fullName: string;
@@ -27,37 +24,40 @@ export type EditPolicyViewQuery$data = {
};
readonly title?: string;
};
readonly policyVersion: {
readonly documentVersion: {
readonly content?: string;
readonly id: string;
};
readonly organization: {
readonly " $fragmentSpreads": FragmentRefs<"PeopleSelector_organization">;
};
};
export type EditPolicyViewQuery = {
response: EditPolicyViewQuery$data;
variables: EditPolicyViewQuery$variables;
export type EditDocumentViewQuery = {
response: EditDocumentViewQuery$data;
variables: EditDocumentViewQuery$variables;
};
const node: ConcreteRequest = (function(){
var v0 = {
"defaultValue": null,
"kind": "LocalArgument",
"name": "organizationId"
"name": "documentId"
},
v1 = {
"defaultValue": null,
"kind": "LocalArgument",
"name": "policyId"
"name": "documentVersionId"
},
v2 = {
"defaultValue": null,
"kind": "LocalArgument",
"name": "policyVersionId"
"name": "organizationId"
},
v3 = [
{
"kind": "Variable",
"name": "id",
"variableName": "policyVersionId"
"variableName": "documentVersionId"
}
],
v4 = {
@@ -78,14 +78,14 @@ v5 = {
"storageKey": null
}
],
"type": "PolicyVersion",
"type": "DocumentVersion",
"abstractKey": null
},
v6 = [
{
"kind": "Variable",
"name": "id",
"variableName": "policyId"
"variableName": "documentId"
}
],
v7 = {
@@ -119,7 +119,7 @@ v8 = {
"storageKey": null
}
],
"type": "Policy",
"type": "Document",
"abstractKey": null
},
v9 = [
@@ -160,10 +160,10 @@ return {
],
"kind": "Fragment",
"metadata": null,
"name": "EditPolicyViewQuery",
"name": "EditDocumentViewQuery",
"selections": [
{
"alias": "policyVersion",
"alias": "documentVersion",
"args": (v3/*: any*/),
"concreteType": null,
"kind": "LinkedField",
@@ -176,7 +176,7 @@ return {
"storageKey": null
},
{
"alias": "policy",
"alias": "document",
"args": (v6/*: any*/),
"concreteType": null,
"kind": "LinkedField",
@@ -211,15 +211,15 @@ return {
"kind": "Request",
"operation": {
"argumentDefinitions": [
(v1/*: any*/),
(v0/*: any*/),
(v2/*: any*/)
(v2/*: any*/),
(v1/*: any*/)
],
"kind": "Operation",
"name": "EditPolicyViewQuery",
"name": "EditDocumentViewQuery",
"selections": [
{
"alias": "policyVersion",
"alias": "documentVersion",
"args": (v3/*: any*/),
"concreteType": null,
"kind": "LinkedField",
@@ -233,7 +233,7 @@ return {
"storageKey": null
},
{
"alias": "policy",
"alias": "document",
"args": (v6/*: any*/),
"concreteType": null,
"kind": "LinkedField",
@@ -355,16 +355,16 @@ return {
]
},
"params": {
"cacheID": "4d586701fc5f2e4976bb87d9be61c594",
"cacheID": "44dcce3514fd8798e8b7bb0c23f9f982",
"id": null,
"metadata": {},
"name": "EditPolicyViewQuery",
"name": "EditDocumentViewQuery",
"operationKind": "query",
"text": "query EditPolicyViewQuery(\n $policyId: ID!\n $organizationId: ID!\n $policyVersionId: ID!\n) {\n policyVersion: node(id: $policyVersionId) {\n __typename\n id\n ... on PolicyVersion {\n content\n }\n }\n policy: node(id: $policyId) {\n __typename\n id\n ... on Policy {\n title\n owner {\n id\n fullName\n }\n }\n }\n organization: node(id: $organizationId) {\n __typename\n ...PeopleSelector_organization\n id\n }\n}\n\nfragment PeopleSelector_organization on Organization {\n id\n peoples(first: 100, orderBy: {direction: ASC, field: FULL_NAME}) {\n edges {\n node {\n id\n fullName\n primaryEmailAddress\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n}\n"
"text": "query EditDocumentViewQuery(\n $documentId: ID!\n $organizationId: ID!\n $documentVersionId: ID!\n) {\n documentVersion: node(id: $documentVersionId) {\n __typename\n id\n ... on DocumentVersion {\n content\n }\n }\n document: node(id: $documentId) {\n __typename\n id\n ... on Document {\n title\n owner {\n id\n fullName\n }\n }\n }\n organization: node(id: $organizationId) {\n __typename\n ...PeopleSelector_organization\n id\n }\n}\n\nfragment PeopleSelector_organization on Organization {\n id\n peoples(first: 100, orderBy: {direction: ASC, field: FULL_NAME}) {\n edges {\n node {\n id\n fullName\n primaryEmailAddress\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n}\n"
}
};
})();
(node as any).hash = "2a934195ed33bd073e3976e8cc19a925";
(node as any).hash = "90a8a811c0c8a08a1112a72537cbd76d";
export default node;

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<290d3c4fb382c7b99c60eeee5cfa8c5a>>
* @generated SignedSource<<70539298ec9a6ffd64b14fa98fdd283e>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -9,27 +9,27 @@
// @ts-nocheck
import { ConcreteRequest } from 'relay-runtime';
export type PolicyStatus = "DRAFT" | "PUBLISHED";
export type CreateDraftPolicyVersionInput = {
policyID: string;
export type DocumentStatus = "DRAFT" | "PUBLISHED";
export type CreateDraftDocumentVersionInput = {
documentID: string;
};
export type ShowPolicyViewCreateDraftMutation$variables = {
input: CreateDraftPolicyVersionInput;
export type ShowDocumentViewCreateDraftMutation$variables = {
input: CreateDraftDocumentVersionInput;
};
export type ShowPolicyViewCreateDraftMutation$data = {
readonly createDraftPolicyVersion: {
readonly policyVersionEdge: {
export type ShowDocumentViewCreateDraftMutation$data = {
readonly createDraftDocumentVersion: {
readonly documentVersionEdge: {
readonly node: {
readonly id: string;
readonly status: PolicyStatus;
readonly status: DocumentStatus;
readonly version: number;
};
};
};
};
export type ShowPolicyViewCreateDraftMutation = {
response: ShowPolicyViewCreateDraftMutation$data;
variables: ShowPolicyViewCreateDraftMutation$variables;
export type ShowDocumentViewCreateDraftMutation = {
response: ShowDocumentViewCreateDraftMutation$data;
variables: ShowDocumentViewCreateDraftMutation$variables;
};
const node: ConcreteRequest = (function(){
@@ -50,23 +50,23 @@ v1 = [
"variableName": "input"
}
],
"concreteType": "CreateDraftPolicyVersionPayload",
"concreteType": "CreateDraftDocumentVersionPayload",
"kind": "LinkedField",
"name": "createDraftPolicyVersion",
"name": "createDraftDocumentVersion",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "PolicyVersionEdge",
"concreteType": "DocumentVersionEdge",
"kind": "LinkedField",
"name": "policyVersionEdge",
"name": "documentVersionEdge",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "PolicyVersion",
"concreteType": "DocumentVersion",
"kind": "LinkedField",
"name": "node",
"plural": false,
@@ -107,7 +107,7 @@ return {
"argumentDefinitions": (v0/*: any*/),
"kind": "Fragment",
"metadata": null,
"name": "ShowPolicyViewCreateDraftMutation",
"name": "ShowDocumentViewCreateDraftMutation",
"selections": (v1/*: any*/),
"type": "Mutation",
"abstractKey": null
@@ -116,20 +116,20 @@ return {
"operation": {
"argumentDefinitions": (v0/*: any*/),
"kind": "Operation",
"name": "ShowPolicyViewCreateDraftMutation",
"name": "ShowDocumentViewCreateDraftMutation",
"selections": (v1/*: any*/)
},
"params": {
"cacheID": "e96d226e93260cd989dc122193fbcbce",
"cacheID": "6ca94698731c59c1d5881a1dd3092277",
"id": null,
"metadata": {},
"name": "ShowPolicyViewCreateDraftMutation",
"name": "ShowDocumentViewCreateDraftMutation",
"operationKind": "mutation",
"text": "mutation ShowPolicyViewCreateDraftMutation(\n $input: CreateDraftPolicyVersionInput!\n) {\n createDraftPolicyVersion(input: $input) {\n policyVersionEdge {\n node {\n id\n version\n status\n }\n }\n }\n}\n"
"text": "mutation ShowDocumentViewCreateDraftMutation(\n $input: CreateDraftDocumentVersionInput!\n) {\n createDraftDocumentVersion(input: $input) {\n documentVersionEdge {\n node {\n id\n version\n status\n }\n }\n }\n}\n"
}
};
})();
(node as any).hash = "49282edca41fc2bdca1a1c6db4b2dc11";
(node as any).hash = "70edae5e8a303869b1686bb02fe4b22e";
export default node;

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<024ec8d58aa5f37adad3ac5e98c15a90>>
* @generated SignedSource<<91076d34daad785f7fce9f5a2cbc4018>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -9,32 +9,32 @@
// @ts-nocheck
import { ConcreteRequest } from 'relay-runtime';
export type PolicyStatus = "DRAFT" | "PUBLISHED";
export type PublishPolicyVersionInput = {
policyId: string;
export type DocumentStatus = "DRAFT" | "PUBLISHED";
export type PublishDocumentVersionInput = {
documentId: string;
};
export type ShowPolicyViewPublishMutation$variables = {
input: PublishPolicyVersionInput;
export type ShowDocumentViewPublishMutation$variables = {
input: PublishDocumentVersionInput;
};
export type ShowPolicyViewPublishMutation$data = {
readonly publishPolicyVersion: {
readonly policy: {
export type ShowDocumentViewPublishMutation$data = {
readonly publishDocumentVersion: {
readonly document: {
readonly currentPublishedVersion: number | null | undefined;
readonly id: string;
};
readonly policyVersion: {
readonly documentVersion: {
readonly id: string;
readonly publishedAt: string | null | undefined;
readonly publishedBy: {
readonly fullName: string;
} | null | undefined;
readonly status: PolicyStatus;
readonly status: DocumentStatus;
};
};
};
export type ShowPolicyViewPublishMutation = {
response: ShowPolicyViewPublishMutation$data;
variables: ShowPolicyViewPublishMutation$variables;
export type ShowDocumentViewPublishMutation = {
response: ShowDocumentViewPublishMutation$data;
variables: ShowDocumentViewPublishMutation$variables;
};
const node: ConcreteRequest = (function(){
@@ -62,9 +62,9 @@ v2 = {
v3 = {
"alias": null,
"args": null,
"concreteType": "Policy",
"concreteType": "Document",
"kind": "LinkedField",
"name": "policy",
"name": "document",
"plural": false,
"selections": [
(v2/*: any*/),
@@ -104,23 +104,23 @@ return {
"argumentDefinitions": (v0/*: any*/),
"kind": "Fragment",
"metadata": null,
"name": "ShowPolicyViewPublishMutation",
"name": "ShowDocumentViewPublishMutation",
"selections": [
{
"alias": null,
"args": (v1/*: any*/),
"concreteType": "PublishPolicyVersionPayload",
"concreteType": "PublishDocumentVersionPayload",
"kind": "LinkedField",
"name": "publishPolicyVersion",
"name": "publishDocumentVersion",
"plural": false,
"selections": [
(v3/*: any*/),
{
"alias": null,
"args": null,
"concreteType": "PolicyVersion",
"concreteType": "DocumentVersion",
"kind": "LinkedField",
"name": "policyVersion",
"name": "documentVersion",
"plural": false,
"selections": [
(v2/*: any*/),
@@ -152,23 +152,23 @@ return {
"operation": {
"argumentDefinitions": (v0/*: any*/),
"kind": "Operation",
"name": "ShowPolicyViewPublishMutation",
"name": "ShowDocumentViewPublishMutation",
"selections": [
{
"alias": null,
"args": (v1/*: any*/),
"concreteType": "PublishPolicyVersionPayload",
"concreteType": "PublishDocumentVersionPayload",
"kind": "LinkedField",
"name": "publishPolicyVersion",
"name": "publishDocumentVersion",
"plural": false,
"selections": [
(v3/*: any*/),
{
"alias": null,
"args": null,
"concreteType": "PolicyVersion",
"concreteType": "DocumentVersion",
"kind": "LinkedField",
"name": "policyVersion",
"name": "documentVersion",
"plural": false,
"selections": [
(v2/*: any*/),
@@ -196,16 +196,16 @@ return {
]
},
"params": {
"cacheID": "2047a011abeae6642230cb7b31080b08",
"cacheID": "794539aab3ddce5d004e72d10b980ca4",
"id": null,
"metadata": {},
"name": "ShowPolicyViewPublishMutation",
"name": "ShowDocumentViewPublishMutation",
"operationKind": "mutation",
"text": "mutation ShowPolicyViewPublishMutation(\n $input: PublishPolicyVersionInput!\n) {\n publishPolicyVersion(input: $input) {\n policy {\n id\n currentPublishedVersion\n }\n policyVersion {\n id\n status\n publishedAt\n publishedBy {\n fullName\n id\n }\n }\n }\n}\n"
"text": "mutation ShowDocumentViewPublishMutation(\n $input: PublishDocumentVersionInput!\n) {\n publishDocumentVersion(input: $input) {\n document {\n id\n currentPublishedVersion\n }\n documentVersion {\n id\n status\n publishedAt\n publishedBy {\n fullName\n id\n }\n }\n }\n}\n"
}
};
})();
(node as any).hash = "d81485a797858cf5f2834cf131d248cc";
(node as any).hash = "66e590251b58966c2ac09625759332f7";
export default node;

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<09afc0438feb0aa6a5a8a95f93d70951>>
* @generated SignedSource<<f7c8f2df8a102d1bd15cb053d146a8b1>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -10,12 +10,12 @@
import { ConcreteRequest } from 'relay-runtime';
import { FragmentRefs } from "relay-runtime";
export type PolicyStatus = "DRAFT" | "PUBLISHED";
export type ShowPolicyViewQuery$variables = {
export type DocumentStatus = "DRAFT" | "PUBLISHED";
export type ShowDocumentViewQuery$variables = {
documentId: string;
organizationId: string;
policyId: string;
};
export type ShowPolicyViewQuery$data = {
export type ShowDocumentViewQuery$data = {
readonly node: {
readonly createdAt?: string;
readonly currentPublishedVersion?: number | null | undefined;
@@ -32,7 +32,7 @@ export type ShowPolicyViewQuery$data = {
readonly publishedBy: {
readonly fullName: string;
} | null | undefined;
readonly status: PolicyStatus;
readonly status: DocumentStatus;
readonly updatedAt: string;
readonly version: number;
};
@@ -45,36 +45,38 @@ export type ShowPolicyViewQuery$data = {
};
readonly title?: string;
readonly updatedAt?: string;
readonly " $fragmentSpreads": FragmentRefs<"SignaturesModal_policyVersions" | "VersionHistoryModal_policyVersions">;
readonly " $fragmentSpreads": FragmentRefs<"SignaturesModal_documentVersions" | "VersionHistoryModal_documentVersions">;
};
readonly organization: {
readonly name?: string;
};
};
export type ShowPolicyViewQuery = {
response: ShowPolicyViewQuery$data;
variables: ShowPolicyViewQuery$variables;
export type ShowDocumentViewQuery = {
response: ShowDocumentViewQuery$data;
variables: ShowDocumentViewQuery$variables;
};
const node: ConcreteRequest = (function(){
var v0 = {
"defaultValue": null,
"kind": "LocalArgument",
"name": "organizationId"
},
v1 = {
"defaultValue": null,
"kind": "LocalArgument",
"name": "policyId"
},
v2 = [
var v0 = [
{
"defaultValue": null,
"kind": "LocalArgument",
"name": "documentId"
},
{
"defaultValue": null,
"kind": "LocalArgument",
"name": "organizationId"
}
],
v1 = [
{
"kind": "Variable",
"name": "id",
"variableName": "organizationId"
}
],
v3 = {
v2 = {
"kind": "InlineFragment",
"selections": [
{
@@ -88,63 +90,63 @@ v3 = {
"type": "Organization",
"abstractKey": null
},
v4 = [
v3 = [
{
"kind": "Variable",
"name": "id",
"variableName": "policyId"
"variableName": "documentId"
}
],
v5 = {
v4 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "id",
"storageKey": null
},
v6 = {
v5 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "title",
"storageKey": null
},
v7 = {
v6 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "description",
"storageKey": null
},
v8 = {
v7 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "createdAt",
"storageKey": null
},
v9 = {
v8 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "updatedAt",
"storageKey": null
},
v10 = {
v9 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "currentPublishedVersion",
"storageKey": null
},
v11 = {
v10 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "fullName",
"storageKey": null
},
v12 = {
v11 = {
"alias": null,
"args": null,
"concreteType": "People",
@@ -152,8 +154,8 @@ v12 = {
"name": "owner",
"plural": false,
"selections": [
(v5/*: any*/),
(v11/*: any*/),
(v4/*: any*/),
(v10/*: any*/),
{
"alias": null,
"args": null,
@@ -164,70 +166,70 @@ v12 = {
],
"storageKey": null
},
v13 = [
v12 = [
{
"kind": "Literal",
"name": "first",
"value": 1
}
],
v14 = {
v13 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "version",
"storageKey": null
},
v15 = {
v14 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "status",
"storageKey": null
},
v16 = {
v15 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "content",
"storageKey": null
},
v17 = {
v16 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "changelog",
"storageKey": null
},
v18 = {
v17 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "publishedAt",
"storageKey": null
},
v19 = {
v18 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "__typename",
"storageKey": null
},
v20 = [
(v11/*: any*/),
(v5/*: any*/)
v19 = [
(v10/*: any*/),
(v4/*: any*/)
],
v21 = {
v20 = {
"alias": null,
"args": null,
"concreteType": "People",
"kind": "LinkedField",
"name": "publishedBy",
"plural": false,
"selections": (v20/*: any*/),
"selections": (v19/*: any*/),
"storageKey": null
},
v22 = [
v21 = [
{
"kind": "Literal",
"name": "first",
@@ -236,58 +238,55 @@ v22 = [
];
return {
"fragment": {
"argumentDefinitions": [
(v0/*: any*/),
(v1/*: any*/)
],
"argumentDefinitions": (v0/*: any*/),
"kind": "Fragment",
"metadata": null,
"name": "ShowPolicyViewQuery",
"name": "ShowDocumentViewQuery",
"selections": [
{
"alias": "organization",
"args": (v2/*: any*/),
"args": (v1/*: any*/),
"concreteType": null,
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
(v3/*: any*/)
(v2/*: any*/)
],
"storageKey": null
},
{
"alias": null,
"args": (v4/*: any*/),
"args": (v3/*: any*/),
"concreteType": null,
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
(v5/*: any*/),
(v4/*: any*/),
{
"kind": "InlineFragment",
"selections": [
(v5/*: any*/),
(v6/*: any*/),
(v7/*: any*/),
(v8/*: any*/),
(v9/*: any*/),
(v10/*: any*/),
(v12/*: any*/),
(v11/*: any*/),
{
"args": null,
"kind": "FragmentSpread",
"name": "SignaturesModal_policyVersions"
"name": "SignaturesModal_documentVersions"
},
{
"args": null,
"kind": "FragmentSpread",
"name": "VersionHistoryModal_policyVersions"
"name": "VersionHistoryModal_documentVersions"
},
{
"alias": "latestVersion",
"args": (v13/*: any*/),
"concreteType": "PolicyVersionConnection",
"args": (v12/*: any*/),
"concreteType": "DocumentVersionConnection",
"kind": "LinkedField",
"name": "versions",
"plural": false,
@@ -295,7 +294,7 @@ return {
{
"alias": null,
"args": null,
"concreteType": "PolicyVersionEdge",
"concreteType": "DocumentVersionEdge",
"kind": "LinkedField",
"name": "edges",
"plural": true,
@@ -303,17 +302,17 @@ return {
{
"alias": null,
"args": null,
"concreteType": "PolicyVersion",
"concreteType": "DocumentVersion",
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
(v5/*: any*/),
(v4/*: any*/),
(v13/*: any*/),
(v14/*: any*/),
(v15/*: any*/),
(v16/*: any*/),
(v17/*: any*/),
(v18/*: any*/),
{
"alias": null,
"args": null,
@@ -322,12 +321,12 @@ return {
"name": "publishedBy",
"plural": false,
"selections": [
(v11/*: any*/)
(v10/*: any*/)
],
"storageKey": null
},
(v8/*: any*/),
(v9/*: any*/)
(v7/*: any*/),
(v8/*: any*/)
],
"storageKey": null
}
@@ -338,7 +337,7 @@ return {
"storageKey": "versions(first:1)"
}
],
"type": "Policy",
"type": "Document",
"abstractKey": null
}
],
@@ -350,48 +349,45 @@ return {
},
"kind": "Request",
"operation": {
"argumentDefinitions": [
(v1/*: any*/),
(v0/*: any*/)
],
"argumentDefinitions": (v0/*: any*/),
"kind": "Operation",
"name": "ShowPolicyViewQuery",
"name": "ShowDocumentViewQuery",
"selections": [
{
"alias": "organization",
"args": (v2/*: any*/),
"args": (v1/*: any*/),
"concreteType": null,
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
(v19/*: any*/),
(v3/*: any*/),
(v5/*: any*/)
(v18/*: any*/),
(v2/*: any*/),
(v4/*: any*/)
],
"storageKey": null
},
{
"alias": null,
"args": (v4/*: any*/),
"args": (v3/*: any*/),
"concreteType": null,
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
(v19/*: any*/),
(v5/*: any*/),
(v18/*: any*/),
(v4/*: any*/),
{
"kind": "InlineFragment",
"selections": [
(v5/*: any*/),
(v6/*: any*/),
(v7/*: any*/),
(v8/*: any*/),
(v9/*: any*/),
(v10/*: any*/),
(v12/*: any*/),
(v11/*: any*/),
{
"alias": "policyVersions",
"alias": "documentVersions",
"args": [
{
"kind": "Literal",
@@ -399,7 +395,7 @@ return {
"value": 10
}
],
"concreteType": "PolicyVersionConnection",
"concreteType": "DocumentVersionConnection",
"kind": "LinkedField",
"name": "versions",
"plural": false,
@@ -407,7 +403,7 @@ return {
{
"alias": null,
"args": null,
"concreteType": "PolicyVersionEdge",
"concreteType": "DocumentVersionEdge",
"kind": "LinkedField",
"name": "edges",
"plural": true,
@@ -415,21 +411,21 @@ return {
{
"alias": null,
"args": null,
"concreteType": "PolicyVersion",
"concreteType": "DocumentVersion",
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
(v5/*: any*/),
(v4/*: any*/),
(v13/*: any*/),
(v14/*: any*/),
(v15/*: any*/),
(v18/*: any*/),
(v9/*: any*/),
(v21/*: any*/),
(v17/*: any*/),
(v8/*: any*/),
(v20/*: any*/),
{
"alias": null,
"args": (v22/*: any*/),
"concreteType": "PolicyVersionSignatureConnection",
"args": (v21/*: any*/),
"concreteType": "DocumentVersionSignatureConnection",
"kind": "LinkedField",
"name": "signatures",
"plural": false,
@@ -437,7 +433,7 @@ return {
{
"alias": null,
"args": null,
"concreteType": "PolicyVersionSignatureEdge",
"concreteType": "DocumentVersionSignatureEdge",
"kind": "LinkedField",
"name": "edges",
"plural": true,
@@ -445,12 +441,12 @@ return {
{
"alias": null,
"args": null,
"concreteType": "PolicyVersionSignature",
"concreteType": "DocumentVersionSignature",
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
(v5/*: any*/),
(v4/*: any*/),
{
"alias": null,
"args": null,
@@ -479,7 +475,7 @@ return {
"kind": "LinkedField",
"name": "signedBy",
"plural": false,
"selections": (v20/*: any*/),
"selections": (v19/*: any*/),
"storageKey": null
},
{
@@ -489,10 +485,10 @@ return {
"kind": "LinkedField",
"name": "requestedBy",
"plural": false,
"selections": (v20/*: any*/),
"selections": (v19/*: any*/),
"storageKey": null
},
(v19/*: any*/)
(v18/*: any*/)
],
"storageKey": null
},
@@ -536,10 +532,10 @@ return {
},
{
"alias": null,
"args": (v22/*: any*/),
"args": (v21/*: any*/),
"filters": null,
"handle": "connection",
"key": "SignaturesModal_policyVersions_signatures",
"key": "SignaturesModal_documentVersions_signatures",
"kind": "LinkedHandle",
"name": "signatures"
}
@@ -561,7 +557,7 @@ return {
"value": 20
}
],
"concreteType": "PolicyVersionConnection",
"concreteType": "DocumentVersionConnection",
"kind": "LinkedField",
"name": "versions",
"plural": false,
@@ -569,7 +565,7 @@ return {
{
"alias": null,
"args": null,
"concreteType": "PolicyVersionEdge",
"concreteType": "DocumentVersionEdge",
"kind": "LinkedField",
"name": "edges",
"plural": true,
@@ -577,19 +573,19 @@ return {
{
"alias": null,
"args": null,
"concreteType": "PolicyVersion",
"concreteType": "DocumentVersion",
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
(v5/*: any*/),
(v4/*: any*/),
(v13/*: any*/),
(v14/*: any*/),
(v15/*: any*/),
(v16/*: any*/),
(v17/*: any*/),
(v18/*: any*/),
(v9/*: any*/),
(v21/*: any*/)
(v8/*: any*/),
(v20/*: any*/)
],
"storageKey": null
}
@@ -601,8 +597,8 @@ return {
},
{
"alias": "latestVersion",
"args": (v13/*: any*/),
"concreteType": "PolicyVersionConnection",
"args": (v12/*: any*/),
"concreteType": "DocumentVersionConnection",
"kind": "LinkedField",
"name": "versions",
"plural": false,
@@ -610,7 +606,7 @@ return {
{
"alias": null,
"args": null,
"concreteType": "PolicyVersionEdge",
"concreteType": "DocumentVersionEdge",
"kind": "LinkedField",
"name": "edges",
"plural": true,
@@ -618,20 +614,20 @@ return {
{
"alias": null,
"args": null,
"concreteType": "PolicyVersion",
"concreteType": "DocumentVersion",
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
(v5/*: any*/),
(v4/*: any*/),
(v13/*: any*/),
(v14/*: any*/),
(v15/*: any*/),
(v16/*: any*/),
(v17/*: any*/),
(v18/*: any*/),
(v21/*: any*/),
(v8/*: any*/),
(v9/*: any*/)
(v20/*: any*/),
(v7/*: any*/),
(v8/*: any*/)
],
"storageKey": null
}
@@ -642,7 +638,7 @@ return {
"storageKey": "versions(first:1)"
}
],
"type": "Policy",
"type": "Document",
"abstractKey": null
}
],
@@ -651,16 +647,16 @@ return {
]
},
"params": {
"cacheID": "643b6f01d617d7f7fa6995afe4960c3c",
"cacheID": "16dd0ba75fa5a1339b59fa3e6bb9a6be",
"id": null,
"metadata": {},
"name": "ShowPolicyViewQuery",
"name": "ShowDocumentViewQuery",
"operationKind": "query",
"text": "query ShowPolicyViewQuery(\n $policyId: ID!\n $organizationId: ID!\n) {\n organization: node(id: $organizationId) {\n __typename\n ... on Organization {\n name\n }\n id\n }\n node(id: $policyId) {\n __typename\n id\n ... on Policy {\n title\n description\n createdAt\n updatedAt\n currentPublishedVersion\n owner {\n id\n fullName\n primaryEmailAddress\n }\n ...SignaturesModal_policyVersions\n ...VersionHistoryModal_policyVersions\n latestVersion: versions(first: 1) {\n edges {\n node {\n id\n version\n status\n content\n changelog\n publishedAt\n publishedBy {\n fullName\n id\n }\n createdAt\n updatedAt\n }\n }\n }\n }\n }\n}\n\nfragment SignaturesModal_policyVersions on Policy {\n title\n policyVersions: versions(first: 10) {\n edges {\n node {\n id\n version\n status\n publishedAt\n updatedAt\n publishedBy {\n fullName\n id\n }\n signatures(first: 100) {\n edges {\n node {\n id\n state\n signedAt\n requestedAt\n signedBy {\n fullName\n id\n }\n requestedBy {\n fullName\n id\n }\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n }\n }\n }\n}\n\nfragment VersionHistoryModal_policyVersions on Policy {\n title\n owner {\n fullName\n id\n }\n versionHistory: versions(first: 20) {\n edges {\n node {\n id\n version\n status\n content\n changelog\n publishedAt\n updatedAt\n publishedBy {\n fullName\n id\n }\n }\n }\n }\n}\n"
"text": "query ShowDocumentViewQuery(\n $documentId: ID!\n $organizationId: ID!\n) {\n organization: node(id: $organizationId) {\n __typename\n ... on Organization {\n name\n }\n id\n }\n node(id: $documentId) {\n __typename\n id\n ... on Document {\n title\n description\n createdAt\n updatedAt\n currentPublishedVersion\n owner {\n id\n fullName\n primaryEmailAddress\n }\n ...SignaturesModal_documentVersions\n ...VersionHistoryModal_documentVersions\n latestVersion: versions(first: 1) {\n edges {\n node {\n id\n version\n status\n content\n changelog\n publishedAt\n publishedBy {\n fullName\n id\n }\n createdAt\n updatedAt\n }\n }\n }\n }\n }\n}\n\nfragment SignaturesModal_documentVersions on Document {\n title\n documentVersions: versions(first: 10) {\n edges {\n node {\n id\n version\n status\n publishedAt\n updatedAt\n publishedBy {\n fullName\n id\n }\n signatures(first: 100) {\n edges {\n node {\n id\n state\n signedAt\n requestedAt\n signedBy {\n fullName\n id\n }\n requestedBy {\n fullName\n id\n }\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n }\n }\n }\n}\n\nfragment VersionHistoryModal_documentVersions on Document {\n title\n owner {\n fullName\n id\n }\n versionHistory: versions(first: 20) {\n edges {\n node {\n id\n version\n status\n content\n changelog\n publishedAt\n updatedAt\n publishedBy {\n fullName\n id\n }\n }\n }\n }\n}\n"
}
};
})();
(node as any).hash = "56b534c0a468ccee10939c7d6c731bfd";
(node as any).hash = "df52189b2fac349ac8588c518a4fce55";
export default node;

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<fda822cf46d8b9a49852a53edd8e363d>>
* @generated SignedSource<<b2f20948e0455b0ec2b79cc461e11242>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -9,9 +9,9 @@
// @ts-nocheck
import { ConcreteRequest } from 'relay-runtime';
export type PolicyVersionSignatureState = "REQUESTED" | "SIGNED";
export type DocumentVersionSignatureState = "REQUESTED" | "SIGNED";
export type RequestSignatureInput = {
policyVersionId: string;
documentVersionId: string;
signatoryId: string;
};
export type SignaturesModalRequestSignatureMutation$variables = {
@@ -20,7 +20,7 @@ export type SignaturesModalRequestSignatureMutation$variables = {
};
export type SignaturesModalRequestSignatureMutation$data = {
readonly requestSignature: {
readonly policyVersionSignatureEdge: {
readonly documentVersionSignatureEdge: {
readonly node: {
readonly id: string;
readonly requestedAt: string;
@@ -32,7 +32,7 @@ export type SignaturesModalRequestSignatureMutation$data = {
readonly fullName: string;
readonly id: string;
};
readonly state: PolicyVersionSignatureState;
readonly state: DocumentVersionSignatureState;
};
};
};
@@ -130,15 +130,15 @@ return {
{
"alias": null,
"args": null,
"concreteType": "PolicyVersionSignatureEdge",
"concreteType": "DocumentVersionSignatureEdge",
"kind": "LinkedField",
"name": "policyVersionSignatureEdge",
"name": "documentVersionSignatureEdge",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "PolicyVersionSignature",
"concreteType": "DocumentVersionSignature",
"kind": "LinkedField",
"name": "node",
"plural": false,
@@ -193,15 +193,15 @@ return {
{
"alias": null,
"args": null,
"concreteType": "PolicyVersionSignatureEdge",
"concreteType": "DocumentVersionSignatureEdge",
"kind": "LinkedField",
"name": "policyVersionSignatureEdge",
"name": "documentVersionSignatureEdge",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "PolicyVersionSignature",
"concreteType": "DocumentVersionSignature",
"kind": "LinkedField",
"name": "node",
"plural": false,
@@ -234,7 +234,7 @@ return {
"handle": "prependEdge",
"key": "",
"kind": "LinkedHandle",
"name": "policyVersionSignatureEdge",
"name": "documentVersionSignatureEdge",
"handleArgs": [
{
"kind": "Variable",
@@ -249,16 +249,16 @@ return {
]
},
"params": {
"cacheID": "4c6a5b6ed95c76c157f8627409a4481b",
"cacheID": "e3606c0b910024de4f5223961365d7cc",
"id": null,
"metadata": {},
"name": "SignaturesModalRequestSignatureMutation",
"operationKind": "mutation",
"text": "mutation SignaturesModalRequestSignatureMutation(\n $input: RequestSignatureInput!\n) {\n requestSignature(input: $input) {\n policyVersionSignatureEdge {\n node {\n id\n state\n signedAt\n requestedAt\n signedBy {\n fullName\n id\n }\n requestedBy {\n fullName\n id\n }\n }\n }\n }\n}\n"
"text": "mutation SignaturesModalRequestSignatureMutation(\n $input: RequestSignatureInput!\n) {\n requestSignature(input: $input) {\n documentVersionSignatureEdge {\n node {\n id\n state\n signedAt\n requestedAt\n signedBy {\n fullName\n id\n }\n requestedBy {\n fullName\n id\n }\n }\n }\n }\n}\n"
}
};
})();
(node as any).hash = "2b17fe4feae2614a6697c63a7334a1cf";
(node as any).hash = "9cb16becf1319afbef638f6461179984";
export default node;

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<ee81b03b50b0bc65932295b898b856d4>>
* @generated SignedSource<<58ee8eb503fb92a720f2033c229e6895>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -9,11 +9,11 @@
// @ts-nocheck
import { ReaderFragment } from 'relay-runtime';
export type PolicyStatus = "DRAFT" | "PUBLISHED";
export type PolicyVersionSignatureState = "REQUESTED" | "SIGNED";
export type DocumentStatus = "DRAFT" | "PUBLISHED";
export type DocumentVersionSignatureState = "REQUESTED" | "SIGNED";
import { FragmentRefs } from "relay-runtime";
export type SignaturesModal_policyVersions$data = {
readonly policyVersions: {
export type SignaturesModal_documentVersions$data = {
readonly documentVersions: {
readonly edges: ReadonlyArray<{
readonly node: {
readonly id: string;
@@ -34,22 +34,22 @@ export type SignaturesModal_policyVersions$data = {
readonly fullName: string;
readonly id: string;
};
readonly state: PolicyVersionSignatureState;
readonly state: DocumentVersionSignatureState;
};
}>;
};
readonly status: PolicyStatus;
readonly status: DocumentStatus;
readonly updatedAt: string;
readonly version: number;
};
}>;
};
readonly title: string;
readonly " $fragmentType": "SignaturesModal_policyVersions";
readonly " $fragmentType": "SignaturesModal_documentVersions";
};
export type SignaturesModal_policyVersions$key = {
readonly " $data"?: SignaturesModal_policyVersions$data;
readonly " $fragmentSpreads": FragmentRefs<"SignaturesModal_policyVersions">;
export type SignaturesModal_documentVersions$key = {
readonly " $data"?: SignaturesModal_documentVersions$data;
readonly " $fragmentSpreads": FragmentRefs<"SignaturesModal_documentVersions">;
};
const node: ReaderFragment = (function(){
@@ -83,7 +83,7 @@ return {
}
]
},
"name": "SignaturesModal_policyVersions",
"name": "SignaturesModal_documentVersions",
"selections": [
{
"alias": null,
@@ -93,7 +93,7 @@ return {
"storageKey": null
},
{
"alias": "policyVersions",
"alias": "documentVersions",
"args": [
{
"kind": "Literal",
@@ -101,7 +101,7 @@ return {
"value": 10
}
],
"concreteType": "PolicyVersionConnection",
"concreteType": "DocumentVersionConnection",
"kind": "LinkedField",
"name": "versions",
"plural": false,
@@ -109,7 +109,7 @@ return {
{
"alias": null,
"args": null,
"concreteType": "PolicyVersionEdge",
"concreteType": "DocumentVersionEdge",
"kind": "LinkedField",
"name": "edges",
"plural": true,
@@ -117,7 +117,7 @@ return {
{
"alias": null,
"args": null,
"concreteType": "PolicyVersion",
"concreteType": "DocumentVersion",
"kind": "LinkedField",
"name": "node",
"plural": false,
@@ -164,15 +164,15 @@ return {
{
"alias": "signatures",
"args": null,
"concreteType": "PolicyVersionSignatureConnection",
"concreteType": "DocumentVersionSignatureConnection",
"kind": "LinkedField",
"name": "__SignaturesModal_policyVersions_signatures_connection",
"name": "__SignaturesModal_documentVersions_signatures_connection",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "PolicyVersionSignatureEdge",
"concreteType": "DocumentVersionSignatureEdge",
"kind": "LinkedField",
"name": "edges",
"plural": true,
@@ -180,7 +180,7 @@ return {
{
"alias": null,
"args": null,
"concreteType": "PolicyVersionSignature",
"concreteType": "DocumentVersionSignature",
"kind": "LinkedField",
"name": "node",
"plural": false,
@@ -288,11 +288,11 @@ return {
"storageKey": "versions(first:10)"
}
],
"type": "Policy",
"type": "Document",
"abstractKey": null
};
})();
(node as any).hash = "da9bf0fc12618afb0f9ec8f2337b9b9b";
(node as any).hash = "1e33c172b26b47a8fe21fdd66c9b4c26";
export default node;

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<21626cad90fcb579071d73bc47e3fb7a>>
* @generated SignedSource<<a88582302d627481e4214530f287e233>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -9,9 +9,9 @@
// @ts-nocheck
import { ReaderFragment } from 'relay-runtime';
export type PolicyStatus = "DRAFT" | "PUBLISHED";
export type DocumentStatus = "DRAFT" | "PUBLISHED";
import { FragmentRefs } from "relay-runtime";
export type VersionHistoryModal_policyVersions$data = {
export type VersionHistoryModal_documentVersions$data = {
readonly owner: {
readonly fullName: string;
};
@@ -26,17 +26,17 @@ export type VersionHistoryModal_policyVersions$data = {
readonly publishedBy: {
readonly fullName: string;
} | null | undefined;
readonly status: PolicyStatus;
readonly status: DocumentStatus;
readonly updatedAt: string;
readonly version: number;
};
}>;
};
readonly " $fragmentType": "VersionHistoryModal_policyVersions";
readonly " $fragmentType": "VersionHistoryModal_documentVersions";
};
export type VersionHistoryModal_policyVersions$key = {
readonly " $data"?: VersionHistoryModal_policyVersions$data;
readonly " $fragmentSpreads": FragmentRefs<"VersionHistoryModal_policyVersions">;
export type VersionHistoryModal_documentVersions$key = {
readonly " $data"?: VersionHistoryModal_documentVersions$data;
readonly " $fragmentSpreads": FragmentRefs<"VersionHistoryModal_documentVersions">;
};
const node: ReaderFragment = (function(){
@@ -53,7 +53,7 @@ return {
"argumentDefinitions": [],
"kind": "Fragment",
"metadata": null,
"name": "VersionHistoryModal_policyVersions",
"name": "VersionHistoryModal_documentVersions",
"selections": [
{
"alias": null,
@@ -81,7 +81,7 @@ return {
"value": 20
}
],
"concreteType": "PolicyVersionConnection",
"concreteType": "DocumentVersionConnection",
"kind": "LinkedField",
"name": "versions",
"plural": false,
@@ -89,7 +89,7 @@ return {
{
"alias": null,
"args": null,
"concreteType": "PolicyVersionEdge",
"concreteType": "DocumentVersionEdge",
"kind": "LinkedField",
"name": "edges",
"plural": true,
@@ -97,7 +97,7 @@ return {
{
"alias": null,
"args": null,
"concreteType": "PolicyVersion",
"concreteType": "DocumentVersion",
"kind": "LinkedField",
"name": "node",
"plural": false,
@@ -171,11 +171,11 @@ return {
"storageKey": "versions(first:20)"
}
],
"type": "Policy",
"type": "Document",
"abstractKey": null
};
})();
(node as any).hash = "09cd9142880cfbb239ce866ea81afe48";
(node as any).hash = "ffcba061a60a144c663de41a48e784d1";
export default node;

View File

@@ -35,13 +35,13 @@ import {
} from "./__generated__/ControlOrganizationMeasuresQuery.graphql";
import { ControlFragment_Control$key } from "./__generated__/ControlFragment_Control.graphql";
import {
ControlLinkedPoliciesQuery$data,
ControlLinkedPoliciesQuery,
} from "./__generated__/ControlLinkedPoliciesQuery.graphql";
ControlLinkedDocumentsQuery$data,
ControlLinkedDocumentsQuery,
} from "./__generated__/ControlLinkedDocumentsQuery.graphql";
import {
ControlOrganizationPoliciesQuery$data,
ControlOrganizationPoliciesQuery,
} from "./__generated__/ControlOrganizationPoliciesQuery.graphql";
ControlOrganizationDocumentsQuery$data,
ControlOrganizationDocumentsQuery,
} from "./__generated__/ControlOrganizationDocumentsQuery.graphql";
const controlFragment = graphql`
fragment ControlFragment_Control on Control {
@@ -96,13 +96,13 @@ const organizationMeasuresQuery = graphql`
}
`;
// Query to fetch linked policies
const linkedPoliciesQuery = graphql`
query ControlLinkedPoliciesQuery($controlId: ID!) {
// Query to fetch linked documents
const linkedDocumentsQuery = graphql`
query ControlLinkedDocumentsQuery($controlId: ID!) {
control: node(id: $controlId) {
id
... on Control {
policies(first: 100) @connection(key: "Control__policies") {
documents(first: 100) @connection(key: "Control__documents") {
edges {
node {
id
@@ -123,13 +123,13 @@ const linkedPoliciesQuery = graphql`
}
`;
// Query to fetch all policies for the organization
const organizationPoliciesQuery = graphql`
query ControlOrganizationPoliciesQuery($organizationId: ID!) {
// Query to fetch all documents for the organization
const organizationDocumentsQuery = graphql`
query ControlOrganizationDocumentsQuery($organizationId: ID!) {
organization: node(id: $organizationId) {
id
... on Organization {
policies(first: 100) @connection(key: "Organization__policies") {
documents(first: 100) @connection(key: "Organization__documents") {
edges {
node {
id
@@ -176,12 +176,12 @@ const deleteMeasureMappingMutation = graphql`
}
`;
// Mutation to create a mapping between a control and a policy
const createPolicyMappingMutation = graphql`
mutation ControlCreatePolicyMappingMutation(
$input: CreateControlPolicyMappingInput!
// Mutation to create a mapping between a control and a document
const createDocumentMappingMutation = graphql`
mutation ControlCreateDocumentMappingMutation(
$input: CreateControlDocumentMappingInput!
) {
createControlPolicyMapping(input: $input) {
createControlDocumentMapping(input: $input) {
controlEdge {
node {
id
@@ -191,13 +191,13 @@ const createPolicyMappingMutation = graphql`
}
`;
// Mutation to delete a mapping between a control and a policy
const deletePolicyMappingMutation = graphql`
mutation ControlDeletePolicyMappingMutation(
$input: DeleteControlPolicyMappingInput!
// Mutation to delete a mapping between a control and a document
const deleteDocumentMappingMutation = graphql`
mutation ControlDeleteDocumentMappingMutation(
$input: DeleteControlDocumentMappingInput!
) {
deleteControlPolicyMapping(input: $input) {
deletedPolicyId
deleteControlDocumentMapping(input: $input) {
deletedDocumentId
}
}
`;
@@ -228,17 +228,17 @@ export function Control({
const [isUnlinkingMeasure, setIsUnlinkingMeasure] = useState(false);
const [categoryFilter, setCategoryFilter] = useState<string | null>(null);
// Policy state
const [isPolicyMappingDialogOpen, setIsPolicyMappingDialogOpen] =
// Document state
const [isDocumentMappingDialogOpen, setIsDocumentMappingDialogOpen] =
useState(false);
const [linkedPoliciesData, setLinkedPoliciesData] =
useState<ControlLinkedPoliciesQuery$data | null>(null);
const [organizationPoliciesData, setOrganizationPoliciesData] =
useState<ControlOrganizationPoliciesQuery$data | null>(null);
const [policySearchQuery, setPolicySearchQuery] = useState("");
const [isLoadingPolicies, setIsLoadingPolicies] = useState(false);
const [isLinkingPolicy, setIsLinkingPolicy] = useState(false);
const [isUnlinkingPolicy, setIsUnlinkingPolicy] = useState(false);
const [linkedDocumentsData, setLinkedDocumentsData] =
useState<ControlLinkedDocumentsQuery$data | null>(null);
const [organizationDocumentsData, setOrganizationDocumentsData] =
useState<ControlOrganizationDocumentsQuery$data | null>(null);
const [documentSearchQuery, setDocumentSearchQuery] = useState("");
const [isLoadingDocuments, setIsLoadingDocuments] = useState(false);
const [isLinkingDocument, setIsLinkingDocument] = useState(false);
const [isUnlinkingDocument, setIsUnlinkingDocument] = useState(false);
// Create mutation hooks
const [commitCreateMeasureMapping] = useMutation(
@@ -247,8 +247,8 @@ export function Control({
const [commitDeleteMeasureMapping] = useMutation(
deleteMeasureMappingMutation
);
const [commitCreatePolicyMapping] = useMutation(createPolicyMappingMutation);
const [commitDeletePolicyMapping] = useMutation(deletePolicyMappingMutation);
const [commitCreateDocumentMapping] = useMutation(createDocumentMappingMutation);
const [commitDeleteDocumentMapping] = useMutation(deleteDocumentMappingMutation);
// Load initial linked measures data
useEffect(() => {
@@ -508,242 +508,242 @@ export function Control({
setIsMeasureMappingDialogOpen(true);
}, [loadMeasuresData]);
// Load initial linked policies data
// Load initial linked documents data
useEffect(() => {
if (control.id) {
setIsLoadingPolicies(true);
fetchQuery<ControlLinkedPoliciesQuery>(environment, linkedPoliciesQuery, {
setIsLoadingDocuments(true);
fetchQuery<ControlLinkedDocumentsQuery>(environment, linkedDocumentsQuery, {
controlId: control.id,
}).subscribe({
next: (data) => {
setLinkedPoliciesData(data);
setIsLoadingPolicies(false);
setLinkedDocumentsData(data);
setIsLoadingDocuments(false);
},
error: (error: Error) => {
console.error("Error loading initial policies:", error);
setIsLoadingPolicies(false);
console.error("Error loading initial documents:", error);
setIsLoadingDocuments(false);
},
});
}
}, [control.id, environment]);
// Load policies data
const loadPoliciesData = useCallback(() => {
// Load documents data
const loadDocumentsData = useCallback(() => {
if (!organizationId || !control.id) return;
setIsLoadingPolicies(true);
setIsLoadingDocuments(true);
// Fetch all policies for the organization
fetchQuery<ControlOrganizationPoliciesQuery>(
// Fetch all documents for the organization
fetchQuery<ControlOrganizationDocumentsQuery>(
environment,
organizationPoliciesQuery,
organizationDocumentsQuery,
{
organizationId,
}
).subscribe({
next: (data) => {
setOrganizationPoliciesData(data);
setOrganizationDocumentsData(data);
},
complete: () => {
// Fetch linked policies for this control
fetchQuery<ControlLinkedPoliciesQuery>(
// Fetch linked documents for this control
fetchQuery<ControlLinkedDocumentsQuery>(
environment,
linkedPoliciesQuery,
linkedDocumentsQuery,
{
controlId: control.id,
}
).subscribe({
next: (data) => {
setLinkedPoliciesData(data);
setIsLoadingPolicies(false);
setLinkedDocumentsData(data);
setIsLoadingDocuments(false);
},
error: (error: Error) => {
console.error("Error fetching linked policies:", error);
setIsLoadingPolicies(false);
console.error("Error fetching linked documents:", error);
setIsLoadingDocuments(false);
toast({
title: "Error",
description: "Failed to load linked policies.",
description: "Failed to load linked documents.",
variant: "destructive",
});
},
});
},
error: (error: Error) => {
console.error("Error fetching organization policies:", error);
setIsLoadingPolicies(false);
console.error("Error fetching organization documents:", error);
setIsLoadingDocuments(false);
toast({
title: "Error",
description: "Failed to load policies.",
description: "Failed to load documents.",
variant: "destructive",
});
},
});
}, [control.id, environment, organizationId, toast]);
// Policy helper functions
const getPolicies = useCallback(() => {
if (!organizationPoliciesData?.organization?.policies?.edges) return [];
return organizationPoliciesData.organization.policies.edges.map(
// Document helper functions
const getDocuments = useCallback(() => {
if (!organizationDocumentsData?.organization?.documents?.edges) return [];
return organizationDocumentsData.organization.documents.edges.map(
(edge) => edge.node
);
}, [organizationPoliciesData]);
}, [organizationDocumentsData]);
const getLinkedPolicies = useCallback(() => {
if (!linkedPoliciesData?.control?.policies?.edges) return [];
return linkedPoliciesData.control.policies.edges.map((edge) => edge.node);
}, [linkedPoliciesData]);
const getLinkedDocuments = useCallback(() => {
if (!linkedDocumentsData?.control?.documents?.edges) return [];
return linkedDocumentsData.control.documents.edges.map((edge) => edge.node);
}, [linkedDocumentsData]);
const isPolicyLinked = useCallback(
(policyId: string) => {
const linkedPolicies = getLinkedPolicies();
return linkedPolicies.some((policy) => policy.id === policyId);
const isDocumentLinked = useCallback(
(documentId: string) => {
const linkedDocuments = getLinkedDocuments();
return linkedDocuments.some((document) => document.id === documentId);
},
[getLinkedPolicies]
[getLinkedDocuments]
);
const filteredPolicies = useCallback(() => {
const policies = getPolicies();
if (!policySearchQuery) return policies;
const filteredDocuments = useCallback(() => {
const documents = getDocuments();
if (!documentSearchQuery) return documents;
return policies.filter((policy) => {
return documents.filter((document) => {
return (
!policySearchQuery ||
policy.title?.toLowerCase().includes(policySearchQuery.toLowerCase()) ||
(policy.description &&
policy.description
!documentSearchQuery ||
document.title?.toLowerCase().includes(documentSearchQuery.toLowerCase()) ||
(document.description &&
document.description
.toLowerCase()
.includes(policySearchQuery.toLowerCase()))
.includes(documentSearchQuery.toLowerCase()))
);
});
}, [getPolicies, policySearchQuery]);
}, [getDocuments, documentSearchQuery]);
// Policy link/unlink handlers
const handleLinkPolicy = useCallback(
(policyId: string) => {
// Document link/unlink handlers
const handleLinkDocument = useCallback(
(documentId: string) => {
if (!control.id) return;
setIsLinkingPolicy(true);
setIsLinkingDocument(true);
commitCreatePolicyMapping({
commitCreateDocumentMapping({
variables: {
input: {
controlId: control.id,
policyId: policyId,
documentId: documentId,
},
},
onCompleted: (_, errors) => {
setIsLinkingPolicy(false);
setIsLinkingDocument(false);
if (errors) {
console.error("Error linking policy:", errors);
console.error("Error linking document:", errors);
toast({
title: "Error",
description: "Failed to link policy. Please try again.",
description: "Failed to link document. Please try again.",
variant: "destructive",
});
return;
}
// Refresh linked policies data
fetchQuery<ControlLinkedPoliciesQuery>(
// Refresh linked documents data
fetchQuery<ControlLinkedDocumentsQuery>(
environment,
linkedPoliciesQuery,
linkedDocumentsQuery,
{
controlId: control.id,
}
).subscribe({
next: (data) => {
setLinkedPoliciesData(data);
setLinkedDocumentsData(data);
},
error: (error: Error) => {
console.error("Error refreshing linked policies:", error);
console.error("Error refreshing linked documents:", error);
},
});
toast({
title: "Success",
description: "Policy successfully linked to control.",
description: "Document successfully linked to control.",
});
},
onError: (error) => {
setIsLinkingPolicy(false);
console.error("Error linking policy:", error);
setIsLinkingDocument(false);
console.error("Error linking document:", error);
toast({
title: "Error",
description: "Failed to link policy. Please try again.",
description: "Failed to link document. Please try again.",
variant: "destructive",
});
},
});
},
[commitCreatePolicyMapping, control.id, environment, toast]
[commitCreateDocumentMapping, control.id, environment, toast]
);
const handleUnlinkPolicy = useCallback(
(policyId: string) => {
const handleUnlinkDocument = useCallback(
(documentId: string) => {
if (!control.id) return;
setIsUnlinkingPolicy(true);
setIsUnlinkingDocument(true);
commitDeletePolicyMapping({
commitDeleteDocumentMapping({
variables: {
input: {
controlId: control.id,
policyId: policyId,
documentId: documentId,
},
},
onCompleted: (_, errors) => {
setIsUnlinkingPolicy(false);
setIsUnlinkingDocument(false);
if (errors) {
console.error("Error unlinking policy:", errors);
console.error("Error unlinking document:", errors);
toast({
title: "Error",
description: "Failed to unlink policy. Please try again.",
description: "Failed to unlink document. Please try again.",
variant: "destructive",
});
return;
}
// Refresh linked policies data
fetchQuery<ControlLinkedPoliciesQuery>(
// Refresh linked documents data
fetchQuery<ControlLinkedDocumentsQuery>(
environment,
linkedPoliciesQuery,
linkedDocumentsQuery,
{
controlId: control.id,
}
).subscribe({
next: (data) => {
setLinkedPoliciesData(data);
setLinkedDocumentsData(data);
},
error: (error: Error) => {
console.error("Error refreshing linked policies:", error);
console.error("Error refreshing linked documents:", error);
},
});
toast({
title: "Success",
description: "Policy successfully unlinked from control.",
description: "Document successfully unlinked from control.",
});
},
onError: (error) => {
setIsUnlinkingPolicy(false);
console.error("Error unlinking policy:", error);
setIsUnlinkingDocument(false);
console.error("Error unlinking document:", error);
toast({
title: "Error",
description: "Failed to unlink policy. Please try again.",
description: "Failed to unlink document. Please try again.",
variant: "destructive",
});
},
});
},
[commitDeletePolicyMapping, control.id, environment, toast]
[commitDeleteDocumentMapping, control.id, environment, toast]
);
const handleOpenPolicyMappingDialog = useCallback(() => {
loadPoliciesData();
setIsPolicyMappingDialogOpen(true);
}, [loadPoliciesData]);
const handleOpenDocumentMappingDialog = useCallback(() => {
loadDocumentsData();
setIsDocumentMappingDialogOpen(true);
}, [loadDocumentsData]);
const formatState = (state: string | undefined): string => {
if (!state) return "Unknown";
@@ -1045,19 +1045,19 @@ export function Control({
</div>
</div>
{/* Policies Section */}
{/* Documents Section */}
<div className="mt-8">
{/* Policy Mapping Dialog */}
{/* Document Mapping Dialog */}
<Dialog
open={isPolicyMappingDialogOpen}
onOpenChange={setIsPolicyMappingDialogOpen}
open={isDocumentMappingDialogOpen}
onOpenChange={setIsDocumentMappingDialogOpen}
>
<DialogContent className="max-w-3xl max-h-[80vh] overflow-hidden flex flex-col">
<DialogHeader>
<DialogTitle>Link Policies to Control</DialogTitle>
<DialogTitle>Link Documents to Control</DialogTitle>
<DialogDescription>
Search and select policies to link to this control. This helps
track which policies address this control.
Search and select documents to link to this control. This helps
track which documents address this control.
</DialogDescription>
</DialogHeader>
@@ -1066,9 +1066,9 @@ export function Control({
<div className="relative">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-tertiary" />
<Input
placeholder="Search policies by name or content..."
value={policySearchQuery}
onChange={(e) => setPolicySearchQuery(e.target.value)}
placeholder="Search documents by name or content..."
value={documentSearchQuery}
onChange={(e) => setDocumentSearchQuery(e.target.value)}
className="w-full pl-10"
/>
</div>
@@ -1076,16 +1076,16 @@ export function Control({
</div>
<div className="flex-1 overflow-hidden">
{isLoadingPolicies ? (
{isLoadingDocuments ? (
<div className="flex items-center justify-center h-full">
<Loader2 className="w-8 h-8 animate-spin text-info" />
<span className="ml-2">Loading policies...</span>
<span className="ml-2">Loading documents...</span>
</div>
) : (
<div className="max-h-[50vh] overflow-y-auto pr-2">
{filteredPolicies().length === 0 ? (
{filteredDocuments().length === 0 ? (
<div className="text-center py-8 text-secondary">
No policies found. Try adjusting your search.
No documents found. Try adjusting your search.
</div>
) : (
<table className="w-full bg-level-1">
@@ -1101,27 +1101,27 @@ export function Control({
</tr>
</thead>
<tbody>
{filteredPolicies().map((policy) => {
const isLinked = isPolicyLinked(policy.id);
{filteredDocuments().map((document) => {
const isLinked = isDocumentLinked(document.id);
return (
<tr
key={policy.id}
key={document.id}
className="border-b hover:bg-invert-bg"
>
<td className="py-3 px-4">
<div className="font-medium">
{policy.title}
{document.title}
</div>
{policy.description && (
{document.description && (
<div className="text-xs text-secondary line-clamp-1 mt-0.5">
{policy.description}
{document.description}
</div>
)}
</td>
<td className="py-3 px-4">
{policy.updatedAt
{document.updatedAt
? new Date(
policy.updatedAt
document.updatedAt
).toLocaleDateString()
: "Not set"}
</td>
@@ -1131,12 +1131,12 @@ export function Control({
variant="outline"
size="sm"
onClick={() =>
handleUnlinkPolicy(policy.id)
handleUnlinkDocument(document.id)
}
disabled={isUnlinkingPolicy}
disabled={isUnlinkingDocument}
className="text-xs h-7 text-danger border-danger-b hover:bg-h-danger-bg"
>
{isUnlinkingPolicy ? (
{isUnlinkingDocument ? (
<Loader2 className="w-4 h-4 animate-spin" />
) : (
<X className="w-4 h-4" />
@@ -1148,12 +1148,12 @@ export function Control({
variant="secondary"
size="sm"
onClick={() =>
handleLinkPolicy(policy.id)
handleLinkDocument(document.id)
}
disabled={isLinkingPolicy}
disabled={isLinkingDocument}
className="text-xs h-7 text-info border-info-b hover:bg-h-info-bg"
>
{isLinkingPolicy ? (
{isLinkingDocument ? (
<Loader2 className="w-4 h-4 animate-spin" />
) : (
<LinkIcon className="w-4 h-4" />
@@ -1173,35 +1173,35 @@ export function Control({
</div>
<DialogFooter className="mt-4">
<Button onClick={() => setIsPolicyMappingDialogOpen(false)}>
<Button onClick={() => setIsDocumentMappingDialogOpen(false)}>
Close
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
{/* Linked Policies List */}
{/* Linked Documents List */}
<div>
<div className="flex justify-between items-center mb-4">
<h3 className="text-xl font-medium text-secondary">Policies</h3>
<h3 className="text-xl font-medium text-secondary">Documents</h3>
<Button
variant="outline"
size="sm"
className="flex items-center gap-1"
onClick={handleOpenPolicyMappingDialog}
onClick={handleOpenDocumentMappingDialog}
>
<LinkIcon className="w-4 h-4" />
<span>Link Policies</span>
<span>Link Documents</span>
</Button>
</div>
{isLoadingPolicies ? (
{isLoadingDocuments ? (
<div className="flex items-center justify-center h-24">
<Loader2 className="w-6 h-6 animate-spin text-info" />
<span className="ml-2">Loading policies...</span>
<span className="ml-2">Loading documents...</span>
</div>
) : linkedPoliciesData?.control?.policies?.edges &&
linkedPoliciesData.control.policies.edges.length > 0 ? (
) : linkedDocumentsData?.control?.documents?.edges &&
linkedDocumentsData.control.documents.edges.length > 0 ? (
<div className="overflow-x-auto border rounded-md">
<table className="w-full">
<thead>
@@ -1214,22 +1214,22 @@ export function Control({
</tr>
</thead>
<tbody>
{getLinkedPolicies().map((policy) => (
{getLinkedDocuments().map((document) => (
<tr
key={policy.id}
key={document.id}
className="border-b hover:bg-invert-bg"
>
<td className="py-3 px-4">
<div className="font-medium">{policy.title}</div>
{policy.description && (
<div className="font-medium">{document.title}</div>
{document.description && (
<div className="text-xs text-secondary line-clamp-1 mt-0.5">
{policy.description}
{document.description}
</div>
)}
</td>
<td className="py-3 px-4">
{policy.updatedAt
? new Date(policy.updatedAt).toLocaleDateString()
{document.updatedAt
? new Date(document.updatedAt).toLocaleDateString()
: "Not set"}
</td>
<td className="py-3 px-4 text-right whitespace-nowrap">
@@ -1241,7 +1241,7 @@ export function Control({
className="text-xs h-7"
>
<Link
to={`/organizations/${organizationId}/policies/${policy.id}`}
to={`/organizations/${organizationId}/documents/${document.id}`}
>
View
</Link>
@@ -1249,8 +1249,8 @@ export function Control({
<Button
variant="outline"
size="sm"
onClick={() => handleUnlinkPolicy(policy.id)}
disabled={isUnlinkingPolicy}
onClick={() => handleUnlinkDocument(document.id)}
disabled={isUnlinkingDocument}
className="text-xs h-7 text-danger border-danger-b hover:bg-h-danger-bg"
>
Unlink
@@ -1264,8 +1264,8 @@ export function Control({
</div>
) : (
<div className="text-center py-8 text-secondary border rounded-md">
No policies linked to this control yet. Click &quot;Link
Policies&quot; to connect some.
No documents linked to this control yet. Click &quot;Link
Documents&quot; to connect some.
</div>
)}
</div>

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<9e8a7131004ab3ef4f20c36c904bd9db>>
* @generated SignedSource<<50d6cda002b6548d568947de29ef3971>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -9,15 +9,15 @@
// @ts-nocheck
import { ConcreteRequest } from 'relay-runtime';
export type CreateControlPolicyMappingInput = {
export type CreateControlDocumentMappingInput = {
controlId: string;
policyId: string;
documentId: string;
};
export type ControlCreatePolicyMappingMutation$variables = {
input: CreateControlPolicyMappingInput;
export type ControlCreateDocumentMappingMutation$variables = {
input: CreateControlDocumentMappingInput;
};
export type ControlCreatePolicyMappingMutation$data = {
readonly createControlPolicyMapping: {
export type ControlCreateDocumentMappingMutation$data = {
readonly createControlDocumentMapping: {
readonly controlEdge: {
readonly node: {
readonly id: string;
@@ -25,9 +25,9 @@ export type ControlCreatePolicyMappingMutation$data = {
};
};
};
export type ControlCreatePolicyMappingMutation = {
response: ControlCreatePolicyMappingMutation$data;
variables: ControlCreatePolicyMappingMutation$variables;
export type ControlCreateDocumentMappingMutation = {
response: ControlCreateDocumentMappingMutation$data;
variables: ControlCreateDocumentMappingMutation$variables;
};
const node: ConcreteRequest = (function(){
@@ -48,9 +48,9 @@ v1 = [
"variableName": "input"
}
],
"concreteType": "CreateControlPolicyMappingPayload",
"concreteType": "CreateControlDocumentMappingPayload",
"kind": "LinkedField",
"name": "createControlPolicyMapping",
"name": "createControlDocumentMapping",
"plural": false,
"selections": [
{
@@ -91,7 +91,7 @@ return {
"argumentDefinitions": (v0/*: any*/),
"kind": "Fragment",
"metadata": null,
"name": "ControlCreatePolicyMappingMutation",
"name": "ControlCreateDocumentMappingMutation",
"selections": (v1/*: any*/),
"type": "Mutation",
"abstractKey": null
@@ -100,20 +100,20 @@ return {
"operation": {
"argumentDefinitions": (v0/*: any*/),
"kind": "Operation",
"name": "ControlCreatePolicyMappingMutation",
"name": "ControlCreateDocumentMappingMutation",
"selections": (v1/*: any*/)
},
"params": {
"cacheID": "d2cf3e45fcc9109399c3b80addb03b20",
"cacheID": "44eba73b10ac45d65d97c96098260898",
"id": null,
"metadata": {},
"name": "ControlCreatePolicyMappingMutation",
"name": "ControlCreateDocumentMappingMutation",
"operationKind": "mutation",
"text": "mutation ControlCreatePolicyMappingMutation(\n $input: CreateControlPolicyMappingInput!\n) {\n createControlPolicyMapping(input: $input) {\n controlEdge {\n node {\n id\n }\n }\n }\n}\n"
"text": "mutation ControlCreateDocumentMappingMutation(\n $input: CreateControlDocumentMappingInput!\n) {\n createControlDocumentMapping(input: $input) {\n controlEdge {\n node {\n id\n }\n }\n }\n}\n"
}
};
})();
(node as any).hash = "9deabb78c91eca04673b23cf5d0b68f5";
(node as any).hash = "8ffd23b4cc3ce09756891fcde03c33cd";
export default node;

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<16f8815c8b1db694fb8cd18be95c3f53>>
* @generated SignedSource<<a39e2bd50fb3d78ac40f96740496fcac>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -9,21 +9,21 @@
// @ts-nocheck
import { ConcreteRequest } from 'relay-runtime';
export type DeleteControlPolicyMappingInput = {
export type DeleteControlDocumentMappingInput = {
controlId: string;
policyId: string;
documentId: string;
};
export type ControlDeletePolicyMappingMutation$variables = {
input: DeleteControlPolicyMappingInput;
export type ControlDeleteDocumentMappingMutation$variables = {
input: DeleteControlDocumentMappingInput;
};
export type ControlDeletePolicyMappingMutation$data = {
readonly deleteControlPolicyMapping: {
readonly deletedPolicyId: string;
export type ControlDeleteDocumentMappingMutation$data = {
readonly deleteControlDocumentMapping: {
readonly deletedDocumentId: string;
};
};
export type ControlDeletePolicyMappingMutation = {
response: ControlDeletePolicyMappingMutation$data;
variables: ControlDeletePolicyMappingMutation$variables;
export type ControlDeleteDocumentMappingMutation = {
response: ControlDeleteDocumentMappingMutation$data;
variables: ControlDeleteDocumentMappingMutation$variables;
};
const node: ConcreteRequest = (function(){
@@ -44,16 +44,16 @@ v1 = [
"variableName": "input"
}
],
"concreteType": "DeleteControlPolicyMappingPayload",
"concreteType": "DeleteControlDocumentMappingPayload",
"kind": "LinkedField",
"name": "deleteControlPolicyMapping",
"name": "deleteControlDocumentMapping",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "deletedPolicyId",
"name": "deletedDocumentId",
"storageKey": null
}
],
@@ -65,7 +65,7 @@ return {
"argumentDefinitions": (v0/*: any*/),
"kind": "Fragment",
"metadata": null,
"name": "ControlDeletePolicyMappingMutation",
"name": "ControlDeleteDocumentMappingMutation",
"selections": (v1/*: any*/),
"type": "Mutation",
"abstractKey": null
@@ -74,20 +74,20 @@ return {
"operation": {
"argumentDefinitions": (v0/*: any*/),
"kind": "Operation",
"name": "ControlDeletePolicyMappingMutation",
"name": "ControlDeleteDocumentMappingMutation",
"selections": (v1/*: any*/)
},
"params": {
"cacheID": "ae87d43859683362f6c7024e7f371425",
"cacheID": "de5f0d5a1db8bf370f893e99e58729df",
"id": null,
"metadata": {},
"name": "ControlDeletePolicyMappingMutation",
"name": "ControlDeleteDocumentMappingMutation",
"operationKind": "mutation",
"text": "mutation ControlDeletePolicyMappingMutation(\n $input: DeleteControlPolicyMappingInput!\n) {\n deleteControlPolicyMapping(input: $input) {\n deletedPolicyId\n }\n}\n"
"text": "mutation ControlDeleteDocumentMappingMutation(\n $input: DeleteControlDocumentMappingInput!\n) {\n deleteControlDocumentMapping(input: $input) {\n deletedDocumentId\n }\n}\n"
}
};
})();
(node as any).hash = "39306cd73fd5af5c603272f0bba45a8f";
(node as any).hash = "d7e9d7d588053130899fe2f52399a99d";
export default node;

View File

@@ -0,0 +1,301 @@
/**
* @generated SignedSource<<0368d1546d67e840ab9a22f8864fde5a>>
* @lightSyntaxTransform
* @nogrep
*/
/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
import { ConcreteRequest } from 'relay-runtime';
export type ControlLinkedDocumentsQuery$variables = {
controlId: string;
};
export type ControlLinkedDocumentsQuery$data = {
readonly control: {
readonly documents?: {
readonly edges: ReadonlyArray<{
readonly node: {
readonly createdAt: string;
readonly currentPublishedVersion: number | null | undefined;
readonly description: string;
readonly id: string;
readonly owner: {
readonly fullName: string;
readonly id: string;
};
readonly title: string;
readonly updatedAt: string;
};
}>;
};
readonly id: string;
};
};
export type ControlLinkedDocumentsQuery = {
response: ControlLinkedDocumentsQuery$data;
variables: ControlLinkedDocumentsQuery$variables;
};
const node: ConcreteRequest = (function(){
var v0 = [
{
"defaultValue": null,
"kind": "LocalArgument",
"name": "controlId"
}
],
v1 = [
{
"kind": "Variable",
"name": "id",
"variableName": "controlId"
}
],
v2 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "id",
"storageKey": null
},
v3 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "__typename",
"storageKey": null
},
v4 = [
{
"alias": null,
"args": null,
"concreteType": "DocumentEdge",
"kind": "LinkedField",
"name": "edges",
"plural": true,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "Document",
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
(v2/*: any*/),
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "title",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "description",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "currentPublishedVersion",
"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,
"concreteType": "People",
"kind": "LinkedField",
"name": "owner",
"plural": false,
"selections": [
(v2/*: any*/),
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "fullName",
"storageKey": null
}
],
"storageKey": null
},
(v3/*: 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": "endCursor",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "hasNextPage",
"storageKey": null
}
],
"storageKey": null
}
],
v5 = [
{
"kind": "Literal",
"name": "first",
"value": 100
}
];
return {
"fragment": {
"argumentDefinitions": (v0/*: any*/),
"kind": "Fragment",
"metadata": null,
"name": "ControlLinkedDocumentsQuery",
"selections": [
{
"alias": "control",
"args": (v1/*: any*/),
"concreteType": null,
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
(v2/*: any*/),
{
"kind": "InlineFragment",
"selections": [
{
"alias": "documents",
"args": null,
"concreteType": "DocumentConnection",
"kind": "LinkedField",
"name": "__Control__documents_connection",
"plural": false,
"selections": (v4/*: any*/),
"storageKey": null
}
],
"type": "Control",
"abstractKey": null
}
],
"storageKey": null
}
],
"type": "Query",
"abstractKey": null
},
"kind": "Request",
"operation": {
"argumentDefinitions": (v0/*: any*/),
"kind": "Operation",
"name": "ControlLinkedDocumentsQuery",
"selections": [
{
"alias": "control",
"args": (v1/*: any*/),
"concreteType": null,
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
(v3/*: any*/),
(v2/*: any*/),
{
"kind": "InlineFragment",
"selections": [
{
"alias": null,
"args": (v5/*: any*/),
"concreteType": "DocumentConnection",
"kind": "LinkedField",
"name": "documents",
"plural": false,
"selections": (v4/*: any*/),
"storageKey": "documents(first:100)"
},
{
"alias": null,
"args": (v5/*: any*/),
"filters": null,
"handle": "connection",
"key": "Control__documents",
"kind": "LinkedHandle",
"name": "documents"
}
],
"type": "Control",
"abstractKey": null
}
],
"storageKey": null
}
]
},
"params": {
"cacheID": "991da084547d2b0790d0698baf953be0",
"id": null,
"metadata": {
"connection": [
{
"count": null,
"cursor": null,
"direction": "forward",
"path": [
"control",
"documents"
]
}
]
},
"name": "ControlLinkedDocumentsQuery",
"operationKind": "query",
"text": "query ControlLinkedDocumentsQuery(\n $controlId: ID!\n) {\n control: node(id: $controlId) {\n __typename\n id\n ... on Control {\n documents(first: 100) {\n edges {\n node {\n id\n title\n description\n currentPublishedVersion\n createdAt\n updatedAt\n owner {\n id\n fullName\n }\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n }\n }\n}\n"
}
};
})();
(node as any).hash = "ec0ce0c9a248664a239bb048da0bb76c";
export default node;

View File

@@ -9,13 +9,13 @@
// @ts-nocheck
import { ConcreteRequest } from 'relay-runtime';
export type ControlLinkedPoliciesQuery$variables = {
export type ControlLinkedDocumentsQuery$variables = {
controlId: string;
};
export type ControlLinkedPoliciesQuery$data = {
export type ControlLinkedDocumentsQuery$data = {
readonly control: {
readonly id: string;
readonly policies?: {
readonly documents?: {
readonly edges: ReadonlyArray<{
readonly node: {
readonly createdAt: string;
@@ -33,9 +33,9 @@ export type ControlLinkedPoliciesQuery$data = {
};
};
};
export type ControlLinkedPoliciesQuery = {
response: ControlLinkedPoliciesQuery$data;
variables: ControlLinkedPoliciesQuery$variables;
export type ControlLinkedDocumentsQuery = {
response: ControlLinkedDocumentsQuery$data;
variables: ControlLinkedDocumentsQuery$variables;
};
const node: ConcreteRequest = (function(){
@@ -71,7 +71,7 @@ v4 = [
{
"alias": null,
"args": null,
"concreteType": "PolicyEdge",
"concreteType": "DocumentEdge",
"kind": "LinkedField",
"name": "edges",
"plural": true,
@@ -79,7 +79,7 @@ v4 = [
{
"alias": null,
"args": null,
"concreteType": "Policy",
"concreteType": "Document",
"kind": "LinkedField",
"name": "node",
"plural": false,
@@ -191,7 +191,7 @@ return {
"argumentDefinitions": (v0/*: any*/),
"kind": "Fragment",
"metadata": null,
"name": "ControlLinkedPoliciesQuery",
"name": "ControlLinkedDocumentsQuery",
"selections": [
{
"alias": "control",
@@ -206,11 +206,11 @@ return {
"kind": "InlineFragment",
"selections": [
{
"alias": "policies",
"alias": "documents",
"args": null,
"concreteType": "PolicyConnection",
"concreteType": "DocumentConnection",
"kind": "LinkedField",
"name": "__Control__policies_connection",
"name": "__Control__documents_connection",
"plural": false,
"selections": (v4/*: any*/),
"storageKey": null
@@ -230,7 +230,7 @@ return {
"operation": {
"argumentDefinitions": (v0/*: any*/),
"kind": "Operation",
"name": "ControlLinkedPoliciesQuery",
"name": "ControlLinkedDocumentsQuery",
"selections": [
{
"alias": "control",
@@ -248,21 +248,21 @@ return {
{
"alias": null,
"args": (v5/*: any*/),
"concreteType": "PolicyConnection",
"concreteType": "DocumentConnection",
"kind": "LinkedField",
"name": "policies",
"name": "documents",
"plural": false,
"selections": (v4/*: any*/),
"storageKey": "policies(first:100)"
"storageKey": "documents(first:100)"
},
{
"alias": null,
"args": (v5/*: any*/),
"filters": null,
"handle": "connection",
"key": "Control__policies",
"key": "Control__documents",
"kind": "LinkedHandle",
"name": "policies"
"name": "documents"
}
],
"type": "Control",
@@ -284,14 +284,14 @@ return {
"direction": "forward",
"path": [
"control",
"policies"
"documents"
]
}
]
},
"name": "ControlLinkedPoliciesQuery",
"name": "ControlLinkedDocumentsQuery",
"operationKind": "query",
"text": "query ControlLinkedPoliciesQuery(\n $controlId: ID!\n) {\n control: node(id: $controlId) {\n __typename\n id\n ... on Control {\n policies(first: 100) {\n edges {\n node {\n id\n title\n description\n currentPublishedVersion\n createdAt\n updatedAt\n owner {\n id\n fullName\n }\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n }\n }\n}\n"
"text": "query ControlLinkedDocumentsQuery(\n $controlId: ID!\n) {\n control: node(id: $controlId) {\n __typename\n id\n ... on Control {\n documents(first: 100) {\n edges {\n node {\n id\n title\n description\n currentPublishedVersion\n createdAt\n updatedAt\n owner {\n id\n fullName\n }\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n }\n }\n}\n"
}
};
})();

View File

@@ -0,0 +1,301 @@
/**
* @generated SignedSource<<2b85a8642da3bc98edc1342bd07841ae>>
* @lightSyntaxTransform
* @nogrep
*/
/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
import { ConcreteRequest } from 'relay-runtime';
export type ControlOrganizationDocumentsQuery$variables = {
organizationId: string;
};
export type ControlOrganizationDocumentsQuery$data = {
readonly organization: {
readonly documents?: {
readonly edges: ReadonlyArray<{
readonly node: {
readonly createdAt: string;
readonly currentPublishedVersion: number | null | undefined;
readonly description: string;
readonly id: string;
readonly owner: {
readonly fullName: string;
readonly id: string;
};
readonly title: string;
readonly updatedAt: string;
};
}>;
};
readonly id: string;
};
};
export type ControlOrganizationDocumentsQuery = {
response: ControlOrganizationDocumentsQuery$data;
variables: ControlOrganizationDocumentsQuery$variables;
};
const node: ConcreteRequest = (function(){
var v0 = [
{
"defaultValue": null,
"kind": "LocalArgument",
"name": "organizationId"
}
],
v1 = [
{
"kind": "Variable",
"name": "id",
"variableName": "organizationId"
}
],
v2 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "id",
"storageKey": null
},
v3 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "__typename",
"storageKey": null
},
v4 = [
{
"alias": null,
"args": null,
"concreteType": "DocumentEdge",
"kind": "LinkedField",
"name": "edges",
"plural": true,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "Document",
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
(v2/*: any*/),
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "title",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "description",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "currentPublishedVersion",
"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,
"concreteType": "People",
"kind": "LinkedField",
"name": "owner",
"plural": false,
"selections": [
(v2/*: any*/),
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "fullName",
"storageKey": null
}
],
"storageKey": null
},
(v3/*: 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": "endCursor",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "hasNextPage",
"storageKey": null
}
],
"storageKey": null
}
],
v5 = [
{
"kind": "Literal",
"name": "first",
"value": 100
}
];
return {
"fragment": {
"argumentDefinitions": (v0/*: any*/),
"kind": "Fragment",
"metadata": null,
"name": "ControlOrganizationDocumentsQuery",
"selections": [
{
"alias": "organization",
"args": (v1/*: any*/),
"concreteType": null,
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
(v2/*: any*/),
{
"kind": "InlineFragment",
"selections": [
{
"alias": "documents",
"args": null,
"concreteType": "DocumentConnection",
"kind": "LinkedField",
"name": "__Organization__documents_connection",
"plural": false,
"selections": (v4/*: any*/),
"storageKey": null
}
],
"type": "Organization",
"abstractKey": null
}
],
"storageKey": null
}
],
"type": "Query",
"abstractKey": null
},
"kind": "Request",
"operation": {
"argumentDefinitions": (v0/*: any*/),
"kind": "Operation",
"name": "ControlOrganizationDocumentsQuery",
"selections": [
{
"alias": "organization",
"args": (v1/*: any*/),
"concreteType": null,
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
(v3/*: any*/),
(v2/*: any*/),
{
"kind": "InlineFragment",
"selections": [
{
"alias": null,
"args": (v5/*: any*/),
"concreteType": "DocumentConnection",
"kind": "LinkedField",
"name": "documents",
"plural": false,
"selections": (v4/*: any*/),
"storageKey": "documents(first:100)"
},
{
"alias": null,
"args": (v5/*: any*/),
"filters": null,
"handle": "connection",
"key": "Organization__documents",
"kind": "LinkedHandle",
"name": "documents"
}
],
"type": "Organization",
"abstractKey": null
}
],
"storageKey": null
}
]
},
"params": {
"cacheID": "6607215b906f016d5f1f7a432f9eb41b",
"id": null,
"metadata": {
"connection": [
{
"count": null,
"cursor": null,
"direction": "forward",
"path": [
"organization",
"documents"
]
}
]
},
"name": "ControlOrganizationDocumentsQuery",
"operationKind": "query",
"text": "query ControlOrganizationDocumentsQuery(\n $organizationId: ID!\n) {\n organization: node(id: $organizationId) {\n __typename\n id\n ... on Organization {\n documents(first: 100) {\n edges {\n node {\n id\n title\n description\n currentPublishedVersion\n createdAt\n updatedAt\n owner {\n id\n fullName\n }\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n }\n }\n}\n"
}
};
})();
(node as any).hash = "99469ffc117f4f1dea4c96eb818d8fc3";
export default node;

View File

@@ -9,13 +9,13 @@
// @ts-nocheck
import { ConcreteRequest } from 'relay-runtime';
export type ControlOrganizationPoliciesQuery$variables = {
export type ControlOrganizationDocumentsQuery$variables = {
organizationId: string;
};
export type ControlOrganizationPoliciesQuery$data = {
export type ControlOrganizationDocumentsQuery$data = {
readonly organization: {
readonly id: string;
readonly policies?: {
readonly documents?: {
readonly edges: ReadonlyArray<{
readonly node: {
readonly createdAt: string;
@@ -33,9 +33,9 @@ export type ControlOrganizationPoliciesQuery$data = {
};
};
};
export type ControlOrganizationPoliciesQuery = {
response: ControlOrganizationPoliciesQuery$data;
variables: ControlOrganizationPoliciesQuery$variables;
export type ControlOrganizationDocumentsQuery = {
response: ControlOrganizationDocumentsQuery$data;
variables: ControlOrganizationDocumentsQuery$variables;
};
const node: ConcreteRequest = (function(){
@@ -71,7 +71,7 @@ v4 = [
{
"alias": null,
"args": null,
"concreteType": "PolicyEdge",
"concreteType": "DocumentEdge",
"kind": "LinkedField",
"name": "edges",
"plural": true,
@@ -79,7 +79,7 @@ v4 = [
{
"alias": null,
"args": null,
"concreteType": "Policy",
"concreteType": "Document",
"kind": "LinkedField",
"name": "node",
"plural": false,
@@ -191,7 +191,7 @@ return {
"argumentDefinitions": (v0/*: any*/),
"kind": "Fragment",
"metadata": null,
"name": "ControlOrganizationPoliciesQuery",
"name": "ControlOrganizationDocumentsQuery",
"selections": [
{
"alias": "organization",
@@ -206,11 +206,11 @@ return {
"kind": "InlineFragment",
"selections": [
{
"alias": "policies",
"alias": "documents",
"args": null,
"concreteType": "PolicyConnection",
"concreteType": "DocumentConnection",
"kind": "LinkedField",
"name": "__Organization__policies_connection",
"name": "__Organization__documents_connection",
"plural": false,
"selections": (v4/*: any*/),
"storageKey": null
@@ -230,7 +230,7 @@ return {
"operation": {
"argumentDefinitions": (v0/*: any*/),
"kind": "Operation",
"name": "ControlOrganizationPoliciesQuery",
"name": "ControlOrganizationDocumentsQuery",
"selections": [
{
"alias": "organization",
@@ -248,21 +248,21 @@ return {
{
"alias": null,
"args": (v5/*: any*/),
"concreteType": "PolicyConnection",
"concreteType": "DocumentConnection",
"kind": "LinkedField",
"name": "policies",
"name": "documents",
"plural": false,
"selections": (v4/*: any*/),
"storageKey": "policies(first:100)"
"storageKey": "documents(first:100)"
},
{
"alias": null,
"args": (v5/*: any*/),
"filters": null,
"handle": "connection",
"key": "Organization__policies",
"key": "Organization__documents",
"kind": "LinkedHandle",
"name": "policies"
"name": "documents"
}
],
"type": "Organization",
@@ -284,14 +284,14 @@ return {
"direction": "forward",
"path": [
"organization",
"policies"
"documents"
]
}
]
},
"name": "ControlOrganizationPoliciesQuery",
"name": "ControlOrganizationDocumentsQuery",
"operationKind": "query",
"text": "query ControlOrganizationPoliciesQuery(\n $organizationId: ID!\n) {\n organization: node(id: $organizationId) {\n __typename\n id\n ... on Organization {\n policies(first: 100) {\n edges {\n node {\n id\n title\n description\n currentPublishedVersion\n createdAt\n updatedAt\n owner {\n id\n fullName\n }\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n }\n }\n}\n"
"text": "query ControlOrganizationDocumentsQuery(\n $organizationId: ID!\n) {\n organization: node(id: $organizationId) {\n __typename\n id\n ... on Organization {\n documents(first: 100) {\n edges {\n node {\n id\n title\n description\n currentPublishedVersion\n createdAt\n updatedAt\n owner {\n id\n fullName\n }\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n }\n }\n}\n"
}
};
})();

View File

@@ -60,11 +60,11 @@ import {
import { ShowRiskViewCreateRiskMeasureMappingMutation } from "./__generated__/ShowRiskViewCreateRiskMeasureMappingMutation.graphql";
import { ShowRiskViewDeleteRiskMeasureMappingMutation } from "./__generated__/ShowRiskViewDeleteRiskMeasureMappingMutation.graphql";
import {
ShowRiskViewOrganizationPoliciesQuery,
ShowRiskViewOrganizationPoliciesQuery$data,
} from "./__generated__/ShowRiskViewOrganizationPoliciesQuery.graphql";
import { ShowRiskViewCreateRiskPolicyMappingMutation } from "./__generated__/ShowRiskViewCreateRiskPolicyMappingMutation.graphql";
import { ShowRiskViewDeleteRiskPolicyMappingMutation } from "./__generated__/ShowRiskViewDeleteRiskPolicyMappingMutation.graphql";
ShowRiskViewOrganizationDocumentsQuery,
ShowRiskViewOrganizationDocumentsQuery$data,
} from "./__generated__/ShowRiskViewOrganizationDocumentsQuery.graphql";
import { ShowRiskViewCreateRiskDocumentMappingMutation } from "./__generated__/ShowRiskViewCreateRiskDocumentMappingMutation.graphql";
import { ShowRiskViewDeleteRiskDocumentMappingMutation } from "./__generated__/ShowRiskViewDeleteRiskDocumentMappingMutation.graphql";
const showRiskViewQuery = graphql`
query ShowRiskViewQuery($riskId: ID!) {
@@ -97,7 +97,7 @@ const showRiskViewQuery = graphql`
}
}
}
policies(first: 100) @connection(key: "Risk__policies") {
documents(first: 100) @connection(key: "Risk__documents") {
edges {
node {
id
@@ -144,13 +144,13 @@ const organizationMeasuresQuery = graphql`
}
`;
// Add query to fetch all policies for the organization
const organizationPoliciesQuery = graphql`
query ShowRiskViewOrganizationPoliciesQuery($organizationId: ID!) {
// Add query to fetch all documents for the organization
const organizationDocumentsQuery = graphql`
query ShowRiskViewOrganizationDocumentsQuery($organizationId: ID!) {
organization: node(id: $organizationId) {
id
... on Organization {
policies(first: 100) @connection(key: "Organization__policies") {
documents(first: 100) @connection(key: "Organization__documents") {
edges {
node {
id
@@ -189,12 +189,12 @@ const deleteRiskMeasureMappingMutation = graphql`
}
`;
// Add mutation to create risk-policy mapping
const createRiskPolicyMappingMutation = graphql`
mutation ShowRiskViewCreateRiskPolicyMappingMutation(
$input: CreateRiskPolicyMappingInput!
// Add mutation to create risk-document mapping
const createRiskDocumentMappingMutation = graphql`
mutation ShowRiskViewCreateRiskDocumentMappingMutation(
$input: CreateRiskDocumentMappingInput!
) {
createRiskPolicyMapping(input: $input) {
createRiskDocumentMapping(input: $input) {
riskEdge {
node {
id
@@ -204,13 +204,13 @@ const createRiskPolicyMappingMutation = graphql`
}
`;
// Add mutation to delete risk-policy mapping
const deleteRiskPolicyMappingMutation = graphql`
mutation ShowRiskViewDeleteRiskPolicyMappingMutation(
$input: DeleteRiskPolicyMappingInput!
// Add mutation to delete risk-document mapping
const deleteRiskDocumentMappingMutation = graphql`
mutation ShowRiskViewDeleteRiskDocumentMappingMutation(
$input: DeleteRiskDocumentMappingInput!
) {
deleteRiskPolicyMapping(input: $input) {
deletedPolicyId
deleteRiskDocumentMapping(input: $input) {
deletedDocumentId
}
}
`;
@@ -377,8 +377,8 @@ function ShowRiskViewContent({
risk.residualImpact!
);
// Fix typing for policies
const policies = risk.policies?.edges?.map((edge) => edge.node) || [];
// Fix typing for documents
const documents = risk.documents?.edges?.map((edge) => edge.node) || [];
// Fix typing for controls
const controls = risk.controls?.edges?.map((edge) => edge.node) || [];
@@ -397,16 +397,16 @@ function ShowRiskViewContent({
Record<string, boolean>
>({});
// Add state for policy mapping dialog
const [isPolicyDialogOpen, setIsPolicyDialogOpen] = useState(false);
const [organizationPoliciesData, setOrganizationPoliciesData] =
useState<ShowRiskViewOrganizationPoliciesQuery$data | null>(null);
const [policySearchQuery, setPolicySearchQuery] = useState("");
const [isLoadingPolicies, setIsLoadingPolicies] = useState(false);
const [linkingPolicies, setLinkingPolicies] = useState<
// Add state for document mapping dialog
const [isDocumentDialogOpen, setIsDocumentDialogOpen] = useState(false);
const [organizationDocumentsData, setOrganizationDocumentsData] =
useState<ShowRiskViewOrganizationDocumentsQuery$data | null>(null);
const [documentSearchQuery, setDocumentSearchQuery] = useState("");
const [isLoadingDocuments, setIsLoadingDocuments] = useState(false);
const [linkingDocuments, setLinkingDocuments] = useState<
Record<string, boolean>
>({});
const [unlinkingPolicies, setUnlinkingPolicies] = useState<
const [unlinkingDocuments, setUnlinkingDocuments] = useState<
Record<string, boolean>
>({});
@@ -420,14 +420,14 @@ function ShowRiskViewContent({
deleteRiskMeasureMappingMutation
);
// Setup policy mutation hooks
const [createRiskPolicyMapping] =
useMutation<ShowRiskViewCreateRiskPolicyMappingMutation>(
createRiskPolicyMappingMutation
// Setup document mutation hooks
const [createRiskDocumentMapping] =
useMutation<ShowRiskViewCreateRiskDocumentMappingMutation>(
createRiskDocumentMappingMutation
);
const [deleteRiskPolicyMapping] =
useMutation<ShowRiskViewDeleteRiskPolicyMappingMutation>(
deleteRiskPolicyMappingMutation
const [deleteRiskDocumentMapping] =
useMutation<ShowRiskViewDeleteRiskDocumentMappingMutation>(
deleteRiskDocumentMappingMutation
);
// Clear filters when dialog closes
@@ -440,14 +440,14 @@ function ShowRiskViewContent({
}
}, [isMeasureDialogOpen]);
// Clear filters when policy dialog closes
// Clear filters when document dialog closes
useEffect(() => {
if (!isPolicyDialogOpen) {
setPolicySearchQuery("");
setLinkingPolicies({});
setUnlinkingPolicies({});
if (!isDocumentDialogOpen) {
setDocumentSearchQuery("");
setLinkingDocuments({});
setUnlinkingDocuments({});
}
}, [isPolicyDialogOpen]);
}, [isDocumentDialogOpen]);
// Load measures data when needed
const loadMeasuresData = useCallback(() => {
@@ -479,30 +479,30 @@ function ShowRiskViewContent({
});
}, [risk.id, environment, organizationId, toast]);
// Load policies data when needed
const loadPoliciesData = useCallback(() => {
// Load documents data when needed
const loadDocumentsData = useCallback(() => {
if (!organizationId || !risk.id) return;
setIsLoadingPolicies(true);
setIsLoadingDocuments(true);
// Fetch all policies for the organization
fetchQuery<ShowRiskViewOrganizationPoliciesQuery>(
// Fetch all documents for the organization
fetchQuery<ShowRiskViewOrganizationDocumentsQuery>(
environment,
organizationPoliciesQuery,
organizationDocumentsQuery,
{
organizationId,
}
).subscribe({
next: (data) => {
setOrganizationPoliciesData(data);
setIsLoadingPolicies(false);
setOrganizationDocumentsData(data);
setIsLoadingDocuments(false);
},
error: (error: Error) => {
console.error("Error fetching organization policies:", error);
setIsLoadingPolicies(false);
console.error("Error fetching organization documents:", error);
setIsLoadingDocuments(false);
toast({
title: "Error",
description: "Failed to load policies.",
description: "Failed to load documents.",
variant: "destructive",
});
},
@@ -554,25 +554,25 @@ function ShowRiskViewContent({
});
}, [getMeasures, measureSearchQuery, categoryFilter]);
// Helper functions for policies
const getPolicies = useCallback(() => {
if (!organizationPoliciesData?.organization?.policies?.edges) return [];
return organizationPoliciesData.organization.policies.edges.map(
// Helper functions for documents
const getDocuments = useCallback(() => {
if (!organizationDocumentsData?.organization?.documents?.edges) return [];
return organizationDocumentsData.organization.documents.edges.map(
(edge) => edge.node
);
}, [organizationPoliciesData]);
}, [organizationDocumentsData]);
const filteredPolicies = useCallback(() => {
const policies = getPolicies();
if (!policySearchQuery) return policies;
const filteredDocuments = useCallback(() => {
const documents = getDocuments();
if (!documentSearchQuery) return documents;
return policies.filter((policy) => {
return documents.filter((document) => {
return (
!policySearchQuery ||
policy.title.toLowerCase().includes(policySearchQuery.toLowerCase())
!documentSearchQuery ||
document.title.toLowerCase().includes(documentSearchQuery.toLowerCase())
);
});
}, [getPolicies, policySearchQuery]);
}, [getDocuments, documentSearchQuery]);
// Handle linking a measure to this risk
const handleLinkMeasure = useCallback(
@@ -718,38 +718,38 @@ function ShowRiskViewContent({
[risk.id, deleteRiskMeasureMapping, toast, environment, loadQuery]
);
// Handle linking a policy to this risk
const handleLinkPolicy = useCallback(
// Handle linking a document to this risk
const handleLinkDocument = useCallback(
(
policy: NonNullable<
document: NonNullable<
NonNullable<
ShowRiskViewOrganizationPoliciesQuery$data["organization"]
>["policies"]
ShowRiskViewOrganizationDocumentsQuery$data["organization"]
>["documents"]
>["edges"][0]["node"]
) => {
if (!risk.id) return;
// Track this specific policy as linking
setLinkingPolicies((prev) => ({ ...prev, [policy.id]: true }));
// Track this specific document as linking
setLinkingDocuments((prev) => ({ ...prev, [document.id]: true }));
createRiskPolicyMapping({
createRiskDocumentMapping({
variables: {
input: {
riskId: risk.id,
policyId: policy.id,
documentId: document.id,
},
},
onCompleted: (_, errors) => {
setLinkingPolicies((prev) => ({
setLinkingDocuments((prev) => ({
...prev,
[policy.id]: false,
[document.id]: false,
}));
if (errors && errors.length > 0) {
console.error("Error linking policy:", errors);
console.error("Error linking document:", errors);
toast({
title: "Error",
description: "Failed to link policy. Please try again.",
description: "Failed to link document. Please try again.",
variant: "destructive",
});
return;
@@ -770,58 +770,58 @@ function ShowRiskViewContent({
toast({
title: "Success",
description: `Linked policy "${policy.title}" to this risk.`,
description: `Linked document "${document.title}" to this risk.`,
});
},
onError: (error) => {
setLinkingPolicies((prev) => ({
setLinkingDocuments((prev) => ({
...prev,
[policy.id]: false,
[document.id]: false,
}));
console.error("Error linking policy:", error);
console.error("Error linking document:", error);
toast({
title: "Error",
description: "Failed to link policy. Please try again.",
description: "Failed to link document. Please try again.",
variant: "destructive",
});
},
});
},
[risk.id, createRiskPolicyMapping, toast, environment, loadQuery]
[risk.id, createRiskDocumentMapping, toast, environment, loadQuery]
);
// Handle unlinking a policy from this risk
const handleUnlinkPolicy = useCallback(
// Handle unlinking a document from this risk
const handleUnlinkDocument = useCallback(
(
policy: NonNullable<
document: NonNullable<
NonNullable<
ShowRiskViewOrganizationPoliciesQuery$data["organization"]
>["policies"]
ShowRiskViewOrganizationDocumentsQuery$data["organization"]
>["documents"]
>["edges"][0]["node"]
) => {
if (!risk.id) return;
// Track this specific policy as unlinking
setUnlinkingPolicies((prev) => ({ ...prev, [policy.id]: true }));
// Track this specific document as unlinking
setUnlinkingDocuments((prev) => ({ ...prev, [document.id]: true }));
deleteRiskPolicyMapping({
deleteRiskDocumentMapping({
variables: {
input: {
riskId: risk.id,
policyId: policy.id,
documentId: document.id,
},
},
onCompleted: (_, errors) => {
setUnlinkingPolicies((prev) => ({
setUnlinkingDocuments((prev) => ({
...prev,
[policy.id]: false,
[document.id]: false,
}));
if (errors && errors.length > 0) {
console.error("Error unlinking policy:", errors);
console.error("Error unlinking document:", errors);
toast({
title: "Error",
description: "Failed to unlink policy. Please try again.",
description: "Failed to unlink document. Please try again.",
variant: "destructive",
});
return;
@@ -842,24 +842,24 @@ function ShowRiskViewContent({
toast({
title: "Success",
description: `Unlinked policy "${policy.title}" from this risk.`,
description: `Unlinked document "${document.title}" from this risk.`,
});
},
onError: (error) => {
setUnlinkingPolicies((prev) => ({
setUnlinkingDocuments((prev) => ({
...prev,
[policy.id]: false,
[document.id]: false,
}));
console.error("Error unlinking policy:", error);
console.error("Error unlinking document:", error);
toast({
title: "Error",
description: "Failed to unlink policy. Please try again.",
description: "Failed to unlink document. Please try again.",
variant: "destructive",
});
},
});
},
[risk.id, deleteRiskPolicyMapping, toast, environment, loadQuery]
[risk.id, deleteRiskDocumentMapping, toast, environment, loadQuery]
);
return (
@@ -1073,7 +1073,7 @@ function ShowRiskViewContent({
<Tabs defaultValue="measures" className="w-full">
<TabsList>
<TabsTrigger value="measures">Measures</TabsTrigger>
<TabsTrigger value="policies">Policies</TabsTrigger>
<TabsTrigger value="documents">Documents</TabsTrigger>
<TabsTrigger value="controls">Controls</TabsTrigger>
</TabsList>
@@ -1133,48 +1133,48 @@ function ShowRiskViewContent({
)}
</TabsContent>
<TabsContent value="policies" className="space-y-4">
<TabsContent value="documents" className="space-y-4">
<div className="flex justify-between items-center mb-4">
<h2 className="text-xl font-semibold">Risk Policies</h2>
<h2 className="text-xl font-semibold">Risk Documents</h2>
<div className="flex space-x-2">
<Button
onClick={() => {
setIsPolicyDialogOpen(true);
loadPoliciesData();
setIsDocumentDialogOpen(true);
loadDocumentsData();
}}
>
<Plus className="mr-2 h-4 w-4" />
Link Policy
Link Document
</Button>
</div>
</div>
{policies.length > 0 ? (
{documents.length > 0 ? (
<div className="rounded-md border">
<Table>
<TableHeader>
<TableRow>
<TableHead className="w-full">Policy</TableHead>
<TableHead className="w-full">Document</TableHead>
<TableHead className="w-20">Actions</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{policies.map((policy) => (
<TableRow key={policy.id}>
{documents.map((document) => (
<TableRow key={document.id}>
<TableCell>
<Link
to={`/organizations/${organizationId}/policies/${policy.id}`}
to={`/organizations/${organizationId}/documents/${document.id}`}
className="font-medium text-blue-600 hover:underline"
>
{policy.title}
{document.title}
</Link>
</TableCell>
<TableCell>
<Button
variant="ghost"
size="icon"
onClick={() => handleUnlinkPolicy(policy)}
disabled={unlinkingPolicies[policy.id] || false}
title="Unlink policy"
onClick={() => handleUnlinkDocument(document)}
disabled={unlinkingDocuments[document.id] || false}
title="Unlink document"
>
<Trash2 className="h-4 w-4 text-danger" />
</Button>
@@ -1186,7 +1186,7 @@ function ShowRiskViewContent({
</div>
) : (
<div className="text-center py-10 text-secondary">
<p>No policies associated with this risk.</p>
<p>No documents associated with this risk.</p>
</div>
)}
</TabsContent>
@@ -1351,15 +1351,15 @@ function ShowRiskViewContent({
</DialogContent>
</Dialog>
{/* Dialog for linking policies */}
<Dialog open={isPolicyDialogOpen} onOpenChange={setIsPolicyDialogOpen}>
{/* Dialog for linking documents */}
<Dialog open={isDocumentDialogOpen} onOpenChange={setIsDocumentDialogOpen}>
<DialogContent className="max-w-3xl">
<DialogHeader>
<div className="flex justify-between items-center">
<div>
<DialogTitle>Manage Risk Policies</DialogTitle>
<DialogTitle>Manage Risk Documents</DialogTitle>
<DialogDescription>
Link or unlink policies to manage this risk.
Link or unlink documents to manage this risk.
</DialogDescription>
</div>
</div>
@@ -1371,45 +1371,45 @@ function ShowRiskViewContent({
<div className="relative">
<Search className="absolute left-2 top-2.5 h-4 w-4 text-tertiary" />
<Input
placeholder="Search policies..."
placeholder="Search documents..."
className="pl-8"
value={policySearchQuery}
onChange={(e) => setPolicySearchQuery(e.target.value)}
value={documentSearchQuery}
onChange={(e) => setDocumentSearchQuery(e.target.value)}
/>
</div>
</div>
</div>
<div className="border rounded-md max-h-96 overflow-y-auto">
{isLoadingPolicies ? (
<div className="p-4 text-center">Loading policies...</div>
) : filteredPolicies().length === 0 ? (
<div className="p-4 text-center">No policies found.</div>
{isLoadingDocuments ? (
<div className="p-4 text-center">Loading documents...</div>
) : filteredDocuments().length === 0 ? (
<div className="p-4 text-center">No documents found.</div>
) : (
<div className="divide-y">
{filteredPolicies().map((policy) => {
{filteredDocuments().map((document) => {
// For each render, recalculate linked status directly against the current risk data
const isLinked = policies.some(
(riskPolicy) => riskPolicy.id === policy.id
const isLinked = documents.some(
(riskDocument) => riskDocument.id === document.id
);
const isLinking = linkingPolicies[policy.id] || false;
const isUnlinking = unlinkingPolicies[policy.id] || false;
const isLinking = linkingDocuments[document.id] || false;
const isUnlinking = unlinkingDocuments[document.id] || false;
return (
<div
key={policy.id}
key={document.id}
className="relative p-4 hover:bg-blue-50 transition-colors duration-150"
>
<div className="flex justify-between items-center">
<div className="flex items-center gap-2">
<h3 className="font-medium">{policy.title}</h3>
<h3 className="font-medium">{document.title}</h3>
</div>
{isLinked ? (
<Button
size="sm"
variant="outline"
disabled={isUnlinking}
onClick={() => handleUnlinkPolicy(policy)}
onClick={() => handleUnlinkDocument(document)}
className="text-red-600 border-red-200 hover:bg-red-50"
>
{isUnlinking ? "Unlinking..." : "Unlink"}
@@ -1418,7 +1418,7 @@ function ShowRiskViewContent({
<Button
size="sm"
disabled={isLinking}
onClick={() => handleLinkPolicy(policy)}
onClick={() => handleLinkDocument(document)}
>
{isLinking ? "Linking..." : "Link"}
</Button>
@@ -1435,7 +1435,7 @@ function ShowRiskViewContent({
<DialogFooter>
<Button
variant="outline"
onClick={() => setIsPolicyDialogOpen(false)}
onClick={() => setIsDocumentDialogOpen(false)}
>
Close
</Button>

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<0a4cb496e3dd17d135eaa689ebba360a>>
* @generated SignedSource<<ef56acc9abd55ffea9aee1093be375bf>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -9,15 +9,15 @@
// @ts-nocheck
import { ConcreteRequest } from 'relay-runtime';
export type CreateRiskPolicyMappingInput = {
policyId: string;
export type CreateRiskDocumentMappingInput = {
documentId: string;
riskId: string;
};
export type ShowRiskViewCreateRiskPolicyMappingMutation$variables = {
input: CreateRiskPolicyMappingInput;
export type ShowRiskViewCreateRiskDocumentMappingMutation$variables = {
input: CreateRiskDocumentMappingInput;
};
export type ShowRiskViewCreateRiskPolicyMappingMutation$data = {
readonly createRiskPolicyMapping: {
export type ShowRiskViewCreateRiskDocumentMappingMutation$data = {
readonly createRiskDocumentMapping: {
readonly riskEdge: {
readonly node: {
readonly id: string;
@@ -25,9 +25,9 @@ export type ShowRiskViewCreateRiskPolicyMappingMutation$data = {
};
};
};
export type ShowRiskViewCreateRiskPolicyMappingMutation = {
response: ShowRiskViewCreateRiskPolicyMappingMutation$data;
variables: ShowRiskViewCreateRiskPolicyMappingMutation$variables;
export type ShowRiskViewCreateRiskDocumentMappingMutation = {
response: ShowRiskViewCreateRiskDocumentMappingMutation$data;
variables: ShowRiskViewCreateRiskDocumentMappingMutation$variables;
};
const node: ConcreteRequest = (function(){
@@ -48,9 +48,9 @@ v1 = [
"variableName": "input"
}
],
"concreteType": "CreateRiskPolicyMappingPayload",
"concreteType": "CreateRiskDocumentMappingPayload",
"kind": "LinkedField",
"name": "createRiskPolicyMapping",
"name": "createRiskDocumentMapping",
"plural": false,
"selections": [
{
@@ -91,7 +91,7 @@ return {
"argumentDefinitions": (v0/*: any*/),
"kind": "Fragment",
"metadata": null,
"name": "ShowRiskViewCreateRiskPolicyMappingMutation",
"name": "ShowRiskViewCreateRiskDocumentMappingMutation",
"selections": (v1/*: any*/),
"type": "Mutation",
"abstractKey": null
@@ -100,20 +100,20 @@ return {
"operation": {
"argumentDefinitions": (v0/*: any*/),
"kind": "Operation",
"name": "ShowRiskViewCreateRiskPolicyMappingMutation",
"name": "ShowRiskViewCreateRiskDocumentMappingMutation",
"selections": (v1/*: any*/)
},
"params": {
"cacheID": "57ab115614eb72c930a05a4496827702",
"cacheID": "d48140a960f31c5ee40799780ad852c0",
"id": null,
"metadata": {},
"name": "ShowRiskViewCreateRiskPolicyMappingMutation",
"name": "ShowRiskViewCreateRiskDocumentMappingMutation",
"operationKind": "mutation",
"text": "mutation ShowRiskViewCreateRiskPolicyMappingMutation(\n $input: CreateRiskPolicyMappingInput!\n) {\n createRiskPolicyMapping(input: $input) {\n riskEdge {\n node {\n id\n }\n }\n }\n}\n"
"text": "mutation ShowRiskViewCreateRiskDocumentMappingMutation(\n $input: CreateRiskDocumentMappingInput!\n) {\n createRiskDocumentMapping(input: $input) {\n riskEdge {\n node {\n id\n }\n }\n }\n}\n"
}
};
})();
(node as any).hash = "ca3e1eee912cd4b7ac2702e4192dd544";
(node as any).hash = "cfd17c609ec34647f8ecafc1b07037ec";
export default node;

View File

@@ -0,0 +1,93 @@
/**
* @generated SignedSource<<77d14d5c3a949757604407daf7ba2600>>
* @lightSyntaxTransform
* @nogrep
*/
/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
import { ConcreteRequest } from 'relay-runtime';
export type DeleteRiskDocumentMappingInput = {
documentId: string;
riskId: string;
};
export type ShowRiskViewDeleteRiskDocumentMappingMutation$variables = {
input: DeleteRiskDocumentMappingInput;
};
export type ShowRiskViewDeleteRiskDocumentMappingMutation$data = {
readonly deleteRiskDocumentMapping: {
readonly deletedDocumentId: string;
};
};
export type ShowRiskViewDeleteRiskDocumentMappingMutation = {
response: ShowRiskViewDeleteRiskDocumentMappingMutation$data;
variables: ShowRiskViewDeleteRiskDocumentMappingMutation$variables;
};
const node: ConcreteRequest = (function(){
var v0 = [
{
"defaultValue": null,
"kind": "LocalArgument",
"name": "input"
}
],
v1 = [
{
"alias": null,
"args": [
{
"kind": "Variable",
"name": "input",
"variableName": "input"
}
],
"concreteType": "DeleteRiskDocumentMappingPayload",
"kind": "LinkedField",
"name": "deleteRiskDocumentMapping",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "deletedDocumentId",
"storageKey": null
}
],
"storageKey": null
}
];
return {
"fragment": {
"argumentDefinitions": (v0/*: any*/),
"kind": "Fragment",
"metadata": null,
"name": "ShowRiskViewDeleteRiskDocumentMappingMutation",
"selections": (v1/*: any*/),
"type": "Mutation",
"abstractKey": null
},
"kind": "Request",
"operation": {
"argumentDefinitions": (v0/*: any*/),
"kind": "Operation",
"name": "ShowRiskViewDeleteRiskDocumentMappingMutation",
"selections": (v1/*: any*/)
},
"params": {
"cacheID": "1cd0845f63cf338aed2c2e565e239306",
"id": null,
"metadata": {},
"name": "ShowRiskViewDeleteRiskDocumentMappingMutation",
"operationKind": "mutation",
"text": "mutation ShowRiskViewDeleteRiskDocumentMappingMutation(\n $input: DeleteRiskDocumentMappingInput!\n) {\n deleteRiskDocumentMapping(input: $input) {\n deletedDocumentId\n }\n}\n"
}
};
})();
(node as any).hash = "b7b61daef6f0544c6b78f69094ad2d82";
export default node;

View File

@@ -1,93 +0,0 @@
/**
* @generated SignedSource<<8f8d550cd2bbc73744ba0b7a97720a49>>
* @lightSyntaxTransform
* @nogrep
*/
/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
import { ConcreteRequest } from 'relay-runtime';
export type DeleteRiskPolicyMappingInput = {
policyId: string;
riskId: string;
};
export type ShowRiskViewDeleteRiskPolicyMappingMutation$variables = {
input: DeleteRiskPolicyMappingInput;
};
export type ShowRiskViewDeleteRiskPolicyMappingMutation$data = {
readonly deleteRiskPolicyMapping: {
readonly deletedPolicyId: string;
};
};
export type ShowRiskViewDeleteRiskPolicyMappingMutation = {
response: ShowRiskViewDeleteRiskPolicyMappingMutation$data;
variables: ShowRiskViewDeleteRiskPolicyMappingMutation$variables;
};
const node: ConcreteRequest = (function(){
var v0 = [
{
"defaultValue": null,
"kind": "LocalArgument",
"name": "input"
}
],
v1 = [
{
"alias": null,
"args": [
{
"kind": "Variable",
"name": "input",
"variableName": "input"
}
],
"concreteType": "DeleteRiskPolicyMappingPayload",
"kind": "LinkedField",
"name": "deleteRiskPolicyMapping",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "deletedPolicyId",
"storageKey": null
}
],
"storageKey": null
}
];
return {
"fragment": {
"argumentDefinitions": (v0/*: any*/),
"kind": "Fragment",
"metadata": null,
"name": "ShowRiskViewDeleteRiskPolicyMappingMutation",
"selections": (v1/*: any*/),
"type": "Mutation",
"abstractKey": null
},
"kind": "Request",
"operation": {
"argumentDefinitions": (v0/*: any*/),
"kind": "Operation",
"name": "ShowRiskViewDeleteRiskPolicyMappingMutation",
"selections": (v1/*: any*/)
},
"params": {
"cacheID": "d10c9ccbc01259fb7fea2bf463d6ecc6",
"id": null,
"metadata": {},
"name": "ShowRiskViewDeleteRiskPolicyMappingMutation",
"operationKind": "mutation",
"text": "mutation ShowRiskViewDeleteRiskPolicyMappingMutation(\n $input: DeleteRiskPolicyMappingInput!\n) {\n deleteRiskPolicyMapping(input: $input) {\n deletedPolicyId\n }\n}\n"
}
};
})();
(node as any).hash = "26177c99b1c35723f913637a1d6ac54b";
export default node;

View File

@@ -0,0 +1,246 @@
/**
* @generated SignedSource<<dfa237a2ce4b8cc6254ff791cb28f9f3>>
* @lightSyntaxTransform
* @nogrep
*/
/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
import { ConcreteRequest } from 'relay-runtime';
export type ShowRiskViewOrganizationDocumentsQuery$variables = {
organizationId: string;
};
export type ShowRiskViewOrganizationDocumentsQuery$data = {
readonly organization: {
readonly documents?: {
readonly edges: ReadonlyArray<{
readonly node: {
readonly id: string;
readonly title: string;
};
}>;
};
readonly id: string;
};
};
export type ShowRiskViewOrganizationDocumentsQuery = {
response: ShowRiskViewOrganizationDocumentsQuery$data;
variables: ShowRiskViewOrganizationDocumentsQuery$variables;
};
const node: ConcreteRequest = (function(){
var v0 = [
{
"defaultValue": null,
"kind": "LocalArgument",
"name": "organizationId"
}
],
v1 = [
{
"kind": "Variable",
"name": "id",
"variableName": "organizationId"
}
],
v2 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "id",
"storageKey": null
},
v3 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "__typename",
"storageKey": null
},
v4 = [
{
"alias": null,
"args": null,
"concreteType": "DocumentEdge",
"kind": "LinkedField",
"name": "edges",
"plural": true,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "Document",
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
(v2/*: any*/),
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "title",
"storageKey": null
},
(v3/*: 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": "endCursor",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "hasNextPage",
"storageKey": null
}
],
"storageKey": null
}
],
v5 = [
{
"kind": "Literal",
"name": "first",
"value": 100
}
];
return {
"fragment": {
"argumentDefinitions": (v0/*: any*/),
"kind": "Fragment",
"metadata": null,
"name": "ShowRiskViewOrganizationDocumentsQuery",
"selections": [
{
"alias": "organization",
"args": (v1/*: any*/),
"concreteType": null,
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
(v2/*: any*/),
{
"kind": "InlineFragment",
"selections": [
{
"alias": "documents",
"args": null,
"concreteType": "DocumentConnection",
"kind": "LinkedField",
"name": "__Organization__documents_connection",
"plural": false,
"selections": (v4/*: any*/),
"storageKey": null
}
],
"type": "Organization",
"abstractKey": null
}
],
"storageKey": null
}
],
"type": "Query",
"abstractKey": null
},
"kind": "Request",
"operation": {
"argumentDefinitions": (v0/*: any*/),
"kind": "Operation",
"name": "ShowRiskViewOrganizationDocumentsQuery",
"selections": [
{
"alias": "organization",
"args": (v1/*: any*/),
"concreteType": null,
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
(v3/*: any*/),
(v2/*: any*/),
{
"kind": "InlineFragment",
"selections": [
{
"alias": null,
"args": (v5/*: any*/),
"concreteType": "DocumentConnection",
"kind": "LinkedField",
"name": "documents",
"plural": false,
"selections": (v4/*: any*/),
"storageKey": "documents(first:100)"
},
{
"alias": null,
"args": (v5/*: any*/),
"filters": null,
"handle": "connection",
"key": "Organization__documents",
"kind": "LinkedHandle",
"name": "documents"
}
],
"type": "Organization",
"abstractKey": null
}
],
"storageKey": null
}
]
},
"params": {
"cacheID": "7006d02b892a141f835e095002b234b1",
"id": null,
"metadata": {
"connection": [
{
"count": null,
"cursor": null,
"direction": "forward",
"path": [
"organization",
"documents"
]
}
]
},
"name": "ShowRiskViewOrganizationDocumentsQuery",
"operationKind": "query",
"text": "query ShowRiskViewOrganizationDocumentsQuery(\n $organizationId: ID!\n) {\n organization: node(id: $organizationId) {\n __typename\n id\n ... on Organization {\n documents(first: 100) {\n edges {\n node {\n id\n title\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n }\n }\n}\n"
}
};
})();
(node as any).hash = "dc0a2a6d494838560e76ac1ff916ecd5";
export default node;

View File

@@ -9,13 +9,13 @@
// @ts-nocheck
import { ConcreteRequest } from 'relay-runtime';
export type ShowRiskViewOrganizationPoliciesQuery$variables = {
export type ShowRiskViewOrganizationDocumentsQuery$variables = {
organizationId: string;
};
export type ShowRiskViewOrganizationPoliciesQuery$data = {
export type ShowRiskViewOrganizationDocumentsQuery$data = {
readonly organization: {
readonly id: string;
readonly policies?: {
readonly documents?: {
readonly edges: ReadonlyArray<{
readonly node: {
readonly id: string;
@@ -25,9 +25,9 @@ export type ShowRiskViewOrganizationPoliciesQuery$data = {
};
};
};
export type ShowRiskViewOrganizationPoliciesQuery = {
response: ShowRiskViewOrganizationPoliciesQuery$data;
variables: ShowRiskViewOrganizationPoliciesQuery$variables;
export type ShowRiskViewOrganizationDocumentsQuery = {
response: ShowRiskViewOrganizationDocumentsQuery$data;
variables: ShowRiskViewOrganizationDocumentsQuery$variables;
};
const node: ConcreteRequest = (function(){
@@ -63,7 +63,7 @@ v4 = [
{
"alias": null,
"args": null,
"concreteType": "PolicyEdge",
"concreteType": "DocumentEdge",
"kind": "LinkedField",
"name": "edges",
"plural": true,
@@ -71,7 +71,7 @@ v4 = [
{
"alias": null,
"args": null,
"concreteType": "Policy",
"concreteType": "Document",
"kind": "LinkedField",
"name": "node",
"plural": false,
@@ -136,7 +136,7 @@ return {
"argumentDefinitions": (v0/*: any*/),
"kind": "Fragment",
"metadata": null,
"name": "ShowRiskViewOrganizationPoliciesQuery",
"name": "ShowRiskViewOrganizationDocumentsQuery",
"selections": [
{
"alias": "organization",
@@ -151,11 +151,11 @@ return {
"kind": "InlineFragment",
"selections": [
{
"alias": "policies",
"alias": "documents",
"args": null,
"concreteType": "PolicyConnection",
"concreteType": "DocumentConnection",
"kind": "LinkedField",
"name": "__Organization__policies_connection",
"name": "__Organization__documents_connection",
"plural": false,
"selections": (v4/*: any*/),
"storageKey": null
@@ -175,7 +175,7 @@ return {
"operation": {
"argumentDefinitions": (v0/*: any*/),
"kind": "Operation",
"name": "ShowRiskViewOrganizationPoliciesQuery",
"name": "ShowRiskViewOrganizationDocumentsQuery",
"selections": [
{
"alias": "organization",
@@ -193,21 +193,21 @@ return {
{
"alias": null,
"args": (v5/*: any*/),
"concreteType": "PolicyConnection",
"concreteType": "DocumentConnection",
"kind": "LinkedField",
"name": "policies",
"name": "documents",
"plural": false,
"selections": (v4/*: any*/),
"storageKey": "policies(first:100)"
"storageKey": "documents(first:100)"
},
{
"alias": null,
"args": (v5/*: any*/),
"filters": null,
"handle": "connection",
"key": "Organization__policies",
"key": "Organization__documents",
"kind": "LinkedHandle",
"name": "policies"
"name": "documents"
}
],
"type": "Organization",
@@ -229,14 +229,14 @@ return {
"direction": "forward",
"path": [
"organization",
"policies"
"documents"
]
}
]
},
"name": "ShowRiskViewOrganizationPoliciesQuery",
"name": "ShowRiskViewOrganizationDocumentsQuery",
"operationKind": "query",
"text": "query ShowRiskViewOrganizationPoliciesQuery(\n $organizationId: ID!\n) {\n organization: node(id: $organizationId) {\n __typename\n id\n ... on Organization {\n policies(first: 100) {\n edges {\n node {\n id\n title\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n }\n }\n}\n"
"text": "query ShowRiskViewOrganizationDocumentsQuery(\n $organizationId: ID!\n) {\n organization: node(id: $organizationId) {\n __typename\n id\n ... on Organization {\n documents(first: 100) {\n edges {\n node {\n id\n title\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n }\n }\n}\n"
}
};
})();

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<48d940f6040fa9375ac915ce8851c658>>
* @generated SignedSource<<d43ef1981a6db6f7813e6587e3eaf192>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -29,6 +29,15 @@ export type ShowRiskViewQuery$data = {
};
readonly createdAt?: string;
readonly description?: string;
readonly documents?: {
readonly edges: ReadonlyArray<{
readonly node: {
readonly createdAt: string;
readonly id: string;
readonly title: string;
};
}>;
};
readonly id: string;
readonly inherentImpact?: number;
readonly inherentLikelihood?: number;
@@ -50,15 +59,6 @@ export type ShowRiskViewQuery$data = {
readonly fullName: string;
readonly id: string;
} | null | undefined;
readonly policies?: {
readonly edges: ReadonlyArray<{
readonly node: {
readonly createdAt: string;
readonly id: string;
readonly title: string;
};
}>;
};
readonly residualImpact?: number;
readonly residualLikelihood?: number;
readonly treatment?: RiskTreatment;
@@ -269,7 +269,7 @@ v18 = [
{
"alias": null,
"args": null,
"concreteType": "PolicyEdge",
"concreteType": "DocumentEdge",
"kind": "LinkedField",
"name": "edges",
"plural": true,
@@ -277,7 +277,7 @@ v18 = [
{
"alias": null,
"args": null,
"concreteType": "Policy",
"concreteType": "Document",
"kind": "LinkedField",
"name": "node",
"plural": false,
@@ -387,11 +387,11 @@ return {
"storageKey": null
},
{
"alias": "policies",
"alias": "documents",
"args": null,
"concreteType": "PolicyConnection",
"concreteType": "DocumentConnection",
"kind": "LinkedField",
"name": "__Risk__policies_connection",
"name": "__Risk__documents_connection",
"plural": false,
"selections": (v18/*: any*/),
"storageKey": null
@@ -469,21 +469,21 @@ return {
{
"alias": null,
"args": (v20/*: any*/),
"concreteType": "PolicyConnection",
"concreteType": "DocumentConnection",
"kind": "LinkedField",
"name": "policies",
"name": "documents",
"plural": false,
"selections": (v18/*: any*/),
"storageKey": "policies(first:100)"
"storageKey": "documents(first:100)"
},
{
"alias": null,
"args": (v20/*: any*/),
"filters": null,
"handle": "connection",
"key": "Risk__policies",
"key": "Risk__documents",
"kind": "LinkedHandle",
"name": "policies"
"name": "documents"
},
{
"alias": null,
@@ -514,7 +514,7 @@ return {
]
},
"params": {
"cacheID": "487765d92cad8c0915ad4fee24d7e702",
"cacheID": "9d1057cf6eac37c07ed841a80a4604b9",
"id": null,
"metadata": {
"connection": [
@@ -533,7 +533,7 @@ return {
"direction": "forward",
"path": [
"node",
"policies"
"documents"
]
},
{
@@ -549,11 +549,11 @@ return {
},
"name": "ShowRiskViewQuery",
"operationKind": "query",
"text": "query ShowRiskViewQuery(\n $riskId: ID!\n) {\n node(id: $riskId) {\n __typename\n id\n ... on Risk {\n name\n description\n treatment\n owner {\n id\n fullName\n }\n inherentLikelihood\n inherentImpact\n residualLikelihood\n residualImpact\n note\n createdAt\n updatedAt\n measures(first: 100) {\n edges {\n node {\n id\n name\n description\n category\n createdAt\n state\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n policies(first: 100) {\n edges {\n node {\n id\n title\n createdAt\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n controls(first: 100) {\n edges {\n node {\n id\n referenceId\n name\n description\n createdAt\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n }\n }\n}\n"
"text": "query ShowRiskViewQuery(\n $riskId: ID!\n) {\n node(id: $riskId) {\n __typename\n id\n ... on Risk {\n name\n description\n treatment\n owner {\n id\n fullName\n }\n inherentLikelihood\n inherentImpact\n residualLikelihood\n residualImpact\n note\n createdAt\n updatedAt\n measures(first: 100) {\n edges {\n node {\n id\n name\n description\n category\n createdAt\n state\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n documents(first: 100) {\n edges {\n node {\n id\n title\n createdAt\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n controls(first: 100) {\n edges {\n node {\n id\n referenceId\n name\n description\n createdAt\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n }\n }\n}\n"
}
};
})();
(node as any).hash = "b8506a3dd8980cd16b5344b990471533";
(node as any).hash = "96763bbbebd3aa29a02fcf1e6e782293";
export default node;

View File

@@ -224,18 +224,18 @@ interface Task {
badges?: Badge[];
}
function TaskCard({ task, onClick, onToggleState }: {
task: Task;
function TaskCard({ task, onClick, onToggleState }: {
task: Task;
onClick?: (task: Task) => void;
onToggleState?: (taskId: string, newState: TaskState) => void;
}) {
return (
<div
<div
className="flex justify-between items-center px-6 py-3 border-b border-gray-100 hover:bg-gray-50 cursor-pointer"
onClick={() => onClick && onClick(task)}
>
<div className="flex gap-4 items-center">
<div
<div
className="flex items-center cursor-pointer"
onClick={(e) => {
e.stopPropagation();
@@ -258,11 +258,11 @@ function TaskCard({ task, onClick, onToggleState }: {
<span className={`text-sm font-medium ${task.state === "DONE" ? "text-gray-500 line-through" : "text-gray-900"}`}>
{task.name}
</span>
<Badge
variant="outline"
className={`text-xs px-1.5 py-0.5 rounded-lg flex items-center gap-1
${task.state === "TODO"
? "bg-green-50 text-green-700 border-green-200"
<Badge
variant="outline"
className={`text-xs px-1.5 py-0.5 rounded-lg flex items-center gap-1
${task.state === "TODO"
? "bg-green-50 text-green-700 border-green-200"
: "bg-gray-50 text-gray-700 border-gray-200"}
`}
>
@@ -282,7 +282,7 @@ function TaskCard({ task, onClick, onToggleState }: {
)}
</div>
</div>
<div className="flex items-center gap-4">
<div className="flex gap-2">
{task.badges && task.badges.map((badge) => (
@@ -324,19 +324,19 @@ function ListTaskContent({
organizationQuery,
organizationQueryRef
);
const organization = data.node;
const [isNewTaskOpen, setIsNewTaskOpen] = useState(false);
// Get organization members
const members = organizationData.organization?.peoples?.edges.map(edge => edge.node) || [];
// Fetch measures
const environment = useRelayEnvironment();
const [measures, setMeasures] = useState<Array<{id: string, name: string, category: string, description?: string, state?: string}>>([]);
const [measuresLoading, setMeasuresLoading] = useState(false);
const [measureSearchQuery, setMeasureSearchQuery] = useState("");
// Task form state
const [taskTitle, setTaskTitle] = useState("");
const [taskDescription, setTaskDescription] = useState("");
@@ -409,7 +409,7 @@ function ListTaskContent({
// Function to convert timeEstimate to ISO8601 duration
const convertToISODuration = (value: number | null, unit: string): string | null => {
if (value === null || value <= 0) return null;
switch (unit) {
case "minutes":
return `PT${value}M`;
@@ -495,7 +495,7 @@ function ListTaskContent({
description="Track your assigned compliance tasks and keep progress on track."
actions={
<>
<Button
<Button
className="bg-gray-900 text-white hover:bg-gray-800 gap-1.5"
onClick={() => setIsNewTaskOpen(true)}
>
@@ -539,15 +539,15 @@ function ListTaskContent({
</TabsTrigger>
</TabsList>
</div>
<TabsContent value="todo">
{todoTasks.length > 0 ? (
todoTasks.map((task) => (
<TaskCard
key={task.id}
task={task}
<TaskCard
key={task.id}
task={task}
onClick={handleTaskClick}
onToggleState={handleToggleTaskState}
onToggleState={handleToggleTaskState}
/>
))
) : (
@@ -556,13 +556,13 @@ function ListTaskContent({
</div>
)}
</TabsContent>
<TabsContent value="done">
{doneTasks.length > 0 ? (
doneTasks.map((task) => (
<TaskCard
key={task.id}
task={task}
<TaskCard
key={task.id}
task={task}
onClick={handleTaskClick}
onToggleState={handleToggleTaskState}
/>
@@ -573,13 +573,13 @@ function ListTaskContent({
</div>
)}
</TabsContent>
<TabsContent value="all">
{allTasks.length > 0 ? (
allTasks.map((task) => (
<TaskCard
key={task.id}
task={task}
<TaskCard
key={task.id}
task={task}
onClick={handleTaskClick}
onToggleState={handleToggleTaskState}
/>
@@ -627,25 +627,25 @@ function ListTaskContent({
onChange={(e) => setTaskDescription(e.target.value)}
/>
</div>
{/* Right Column - Properties */}
<div className="w-[400px] p-6">
<h3 className="text-base font-medium text-gray-900 mb-4">Properties</h3>
<div className="space-y-4">
{/* Status */}
<div className="flex justify-between items-center border-b border-[rgba(2,42,2,0.08)] py-3">
<Label className="text-sm font-medium text-gray-500">Status</Label>
<div className="bg-[rgba(0,39,0,0.05)] px-[10px] py-[6px] rounded-lg flex items-center gap-1.5">
<img
src="/images/radio-unchecked.svg"
alt="Radio"
<img
src="/images/radio-unchecked.svg"
alt="Radio"
className="w-4 h-4"
/>
<span className="text-sm font-medium">To do</span>
</div>
</div>
{/* Assignee */}
<div className="flex justify-between items-center border-b border-[rgba(2,42,2,0.08)] py-3">
<Label className="text-sm font-medium text-gray-500">Assignee</Label>
@@ -671,16 +671,16 @@ function ListTaskContent({
</div>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-[220px] max-h-[300px] overflow-y-auto">
<DropdownMenuItem
<DropdownMenuItem
className="flex items-center gap-2 cursor-pointer"
onClick={() => setAssignee(null)}
>
<User className="h-4 w-4 text-gray-500" />
<span>Unassigned</span>
</DropdownMenuItem>
{members.map(member => (
<DropdownMenuItem
<DropdownMenuItem
key={member.id}
className="flex items-center gap-2 cursor-pointer"
onClick={() => setAssignee({id: member.id, fullName: member.fullName})}
@@ -696,7 +696,7 @@ function ListTaskContent({
</DropdownMenuContent>
</DropdownMenu>
</div>
{/* Measure */}
<div className="flex justify-between items-center border-b border-[rgba(2,42,2,0.08)] py-3">
<Label className="text-sm font-medium text-gray-500">Measure</Label>
@@ -723,8 +723,8 @@ function ListTaskContent({
/>
</div>
</div>
<DropdownMenuItem
<DropdownMenuItem
className="flex items-center gap-2 cursor-pointer"
onClick={() => {
setSelectedMeasure(null);
@@ -733,14 +733,14 @@ function ListTaskContent({
>
<span>No measure</span>
</DropdownMenuItem>
{measuresLoading ? (
<div className="p-3 text-center text-sm text-gray-500">
Loading measures...
</div>
) : filteredMeasures.length > 0 ? (
filteredMeasures.map(measure => (
<DropdownMenuItem
<DropdownMenuItem
key={measure.id}
className="flex items-start gap-2 cursor-pointer py-2"
onClick={() => {
@@ -764,15 +764,15 @@ function ListTaskContent({
</DropdownMenuContent>
</DropdownMenu>
</div>
{/* Time estimate */}
<div className="flex justify-between items-center border-b border-[rgba(2,42,2,0.08)] py-3">
<Label className="text-sm font-medium text-gray-500">Time estimate</Label>
<div className="flex items-center gap-2">
{timeEstimate === null ? (
<Button
variant="ghost"
size="sm"
<Button
variant="ghost"
size="sm"
className="h-8 px-2"
onClick={() => setTimeEstimate(0)}
>
@@ -789,9 +789,9 @@ function ListTaskContent({
/>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="outline"
size="sm"
<Button
variant="outline"
size="sm"
className="h-8 flex items-center gap-1"
>
<span>{timeUnit}</span>
@@ -813,10 +813,10 @@ function ListTaskContent({
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
<Button
variant="ghost"
size="sm"
className="h-8 p-0"
<Button
variant="ghost"
size="sm"
className="h-8 p-0"
onClick={() => setTimeEstimate(null)}
>
<X className="w-4 h-4" />
@@ -831,14 +831,14 @@ function ListTaskContent({
{/* Modal Footer */}
<div className="flex justify-between items-center px-6 py-4 border-t border-[rgba(2,42,2,0.08)]">
<Button
variant="outline"
<Button
variant="outline"
className="h-8 flex items-center gap-1.5 border-[rgba(2,42,2,0.08)]"
>
<img src="/images/attachment.svg" alt="Attachment" className="w-4 h-4" />
Upload evidence
</Button>
<div className="flex items-center gap-2">
<Button
variant="outline"

View File

@@ -1,9 +1,9 @@
/* Policy Content Styles */
.policy-content {
/* Document Content Styles */
.document-content {
line-height: 1.6;
}
.policy-content h1 {
.document-content h1 {
font-size: 2rem;
font-weight: 700;
margin-top: 1.5rem;
@@ -11,7 +11,7 @@
color: hsl(var(--foreground));
}
.policy-content h2 {
.document-content h2 {
font-size: 1.5rem;
font-weight: 600;
margin-top: 1.5rem;
@@ -19,7 +19,7 @@
color: hsl(var(--foreground));
}
.policy-content h3 {
.document-content h3 {
font-size: 1.25rem;
font-weight: 600;
margin-top: 1.25rem;
@@ -27,7 +27,7 @@
color: hsl(var(--foreground));
}
.policy-content h4 {
.document-content h4 {
font-size: 1.125rem;
font-weight: 600;
margin-top: 1rem;
@@ -35,38 +35,38 @@
color: hsl(var(--foreground));
}
.policy-content p {
.document-content p {
margin-bottom: 1rem;
}
.policy-content ul,
.policy-content ol {
.document-content ul,
.document-content ol {
margin-left: 1.5rem;
margin-bottom: 1rem;
}
.policy-content ul {
.document-content ul {
list-style-type: disc;
}
.policy-content ol {
.document-content ol {
list-style-type: decimal;
}
.policy-content li {
.document-content li {
margin-bottom: 0.5rem;
}
.policy-content a {
.document-content a {
color: hsl(var(--primary));
text-decoration: underline;
}
.policy-content a:hover {
.document-content a:hover {
text-decoration: none;
}
.policy-content blockquote {
.document-content blockquote {
border-left: 4px solid hsl(var(--solid-b));
padding-left: 1rem;
margin-left: 0;
@@ -75,7 +75,7 @@
color: hsl(var(--tertiary));
}
.policy-content code {
.document-content code {
font-family: monospace;
background-color: hsl(var(--invert-bg));
padding: 0.2rem 0.4rem;
@@ -83,7 +83,7 @@
font-size: 0.875em;
}
.policy-content pre {
.document-content pre {
background-color: hsl(var(--invert-bg));
padding: 1rem;
border-radius: 0.5rem;
@@ -91,37 +91,37 @@
margin-bottom: 1rem;
}
.policy-content pre code {
.document-content pre code {
background-color: transparent;
padding: 0;
border-radius: 0;
}
.policy-content table {
.document-content table {
width: 100%;
border-collapse: collapse;
margin-bottom: 1rem;
}
.policy-content th,
.policy-content td {
.document-content th,
.document-content td {
border: 1px solid hsl(var(--solid-b));
padding: 0.5rem;
}
.policy-content th {
.document-content th {
background-color: hsl(var(--invert-bg));
font-weight: 600;
}
.policy-content img {
.document-content img {
max-width: 100%;
height: auto;
border-radius: 0.5rem;
}
.policy-content hr {
.document-content hr {
border: 0;
border-top: 1px solid hsl(var(--solid-b));
margin: 1.5rem 0;
}
}

View File

@@ -56,11 +56,11 @@ func (c Control) CursorKey(orderBy ControlOrderField) page.CursorKey {
panic(fmt.Sprintf("unsupported order by: %s", orderBy))
}
func (c *Controls) LoadByPolicyID(
func (c *Controls) LoadByDocumentID(
ctx context.Context,
conn pg.Conn,
scope Scoper,
policyID gid.GID,
documentID gid.GID,
cursor *page.Cursor[ControlOrderField],
) error {
q := `
@@ -77,9 +77,9 @@ WITH ctrl AS (
FROM
controls c
INNER JOIN
controls_policies cp ON c.id = cp.control_id
controls_documents cp ON c.id = cp.control_id
WHERE
cp.policy_id = @policy_id
cp.document_id = @document_id
)
SELECT
id,
@@ -97,7 +97,7 @@ WHERE %s
`
q = fmt.Sprintf(q, scope.SQLFragment(), cursor.SQLFragment())
args := pgx.NamedArgs{"policy_id": policyID}
args := pgx.NamedArgs{"document_id": documentID}
maps.Copy(args, scope.SQLArguments())
maps.Copy(args, cursor.SQLArguments())
@@ -196,9 +196,9 @@ WITH ctrl AS (
FROM
controls c
LEFT JOIN
controls_policies cp ON c.id = cp.control_id
controls_documents cp ON c.id = cp.control_id
LEFT JOIN
risks_policies rp ON cp.policy_id = rp.policy_id
risks_documents rp ON cp.document_id = rp.document_id
LEFT JOIN
controls_measures cm ON c.id = cm.control_id
LEFT JOIN
@@ -449,7 +449,7 @@ UPDATE controls SET
updated_at = @updated_at
WHERE %s
AND id = @control_id
RETURNING
RETURNING
id,
framework_id,
tenant_id,

View File

@@ -26,67 +26,67 @@ import (
)
type (
ControlPolicy struct {
ControlID gid.GID `db:"control_id"`
PolicyID gid.GID `db:"policy_id"`
TenantID gid.TenantID `db:"tenant_id"`
CreatedAt time.Time `db:"created_at"`
ControlDocument struct {
ControlID gid.GID `db:"control_id"`
DocumentID gid.GID `db:"document_id"`
TenantID gid.TenantID `db:"tenant_id"`
CreatedAt time.Time `db:"created_at"`
}
ControlPolicies []*ControlPolicy
ControlDocuments []*ControlDocument
)
func (cp ControlPolicy) Insert(
func (cp ControlDocument) Insert(
ctx context.Context,
conn pg.Conn,
scope Scoper,
) error {
q := `
INSERT INTO
controls_policies (
controls_documents (
control_id,
policy_id,
document_id,
tenant_id,
created_at
)
VALUES (
@control_id,
@policy_id,
@document_id,
@tenant_id,
@created_at
);
`
args := pgx.StrictNamedArgs{
"control_id": cp.ControlID,
"policy_id": cp.PolicyID,
"tenant_id": scope.GetTenantID(),
"created_at": cp.CreatedAt,
"control_id": cp.ControlID,
"document_id": cp.DocumentID,
"tenant_id": scope.GetTenantID(),
"created_at": cp.CreatedAt,
}
_, err := conn.Exec(ctx, q, args)
return err
}
func (cp ControlPolicy) Delete(
func (cp ControlDocument) Delete(
ctx context.Context,
conn pg.Conn,
scope Scoper,
controlID gid.GID,
policyID gid.GID,
documentID gid.GID,
) error {
q := `
DELETE
FROM
controls_policies
controls_documents
WHERE
%s
AND control_id = @control_id
AND policy_id = @policy_id;
AND document_id = @document_id;
`
args := pgx.StrictNamedArgs{
"control_id": controlID,
"policy_id": policyID,
"control_id": controlID,
"document_id": documentID,
}
maps.Copy(args, scope.SQLArguments())

View File

@@ -27,7 +27,7 @@ import (
)
type (
Policy struct {
Document struct {
ID gid.GID `db:"id"`
OrganizationID gid.GID `db:"organization_id"`
OwnerID gid.GID `db:"owner_id"`
@@ -37,25 +37,25 @@ type (
UpdatedAt time.Time `db:"updated_at"`
}
Policies []*Policy
Documents []*Document
)
func (p Policy) CursorKey(orderBy PolicyOrderField) page.CursorKey {
func (p Document) CursorKey(orderBy DocumentOrderField) page.CursorKey {
switch orderBy {
case PolicyOrderFieldCreatedAt:
case DocumentOrderFieldCreatedAt:
return page.NewCursorKey(p.ID, p.CreatedAt)
case PolicyOrderFieldTitle:
case DocumentOrderFieldTitle:
return page.NewCursorKey(p.ID, p.Title)
}
panic(fmt.Sprintf("unsupported order by: %s", orderBy))
}
func (p *Policy) LoadByID(
func (p *Document) LoadByID(
ctx context.Context,
conn pg.Conn,
scope Scoper,
policyID gid.GID,
documentID gid.GID,
) error {
q := `
SELECT
@@ -67,39 +67,39 @@ SELECT
created_at,
updated_at
FROM
policies
documents
WHERE
%s
AND id = @policy_id
AND id = @document_id
LIMIT 1;
`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{"policy_id": policyID}
args := pgx.StrictNamedArgs{"document_id": documentID}
maps.Copy(args, scope.SQLArguments())
rows, err := conn.Query(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot query policies: %w", err)
return fmt.Errorf("cannot query documents: %w", err)
}
policy, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[Policy])
document, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[Document])
if err != nil {
return fmt.Errorf("cannot collect policy: %w", err)
return fmt.Errorf("cannot collect document: %w", err)
}
*p = policy
*p = document
return nil
}
func (p *Policies) LoadByOrganizationID(
func (p *Documents) LoadByOrganizationID(
ctx context.Context,
conn pg.Conn,
scope Scoper,
organizationID gid.GID,
cursor *page.Cursor[PolicyOrderField],
cursor *page.Cursor[DocumentOrderField],
) error {
q := `
SELECT
@@ -111,7 +111,7 @@ SELECT
created_at,
updated_at
FROM
policies
documents
WHERE
%s
AND organization_id = @organization_id
@@ -126,27 +126,27 @@ WHERE
rows, err := conn.Query(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot query policies: %w", err)
return fmt.Errorf("cannot query documents: %w", err)
}
policies, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[Policy])
documents, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[Document])
if err != nil {
return fmt.Errorf("cannot collect policies: %w", err)
return fmt.Errorf("cannot collect documents: %w", err)
}
*p = policies
*p = documents
return nil
}
func (p Policy) Insert(
func (p Document) Insert(
ctx context.Context,
conn pg.Conn,
scope Scoper,
) error {
q := `
INSERT INTO
policies (
documents (
tenant_id,
id,
organization_id,
@@ -158,7 +158,7 @@ INSERT INTO
)
VALUES (
@tenant_id,
@policy_id,
@document_id,
@organization_id,
@owner_id,
@title,
@@ -170,7 +170,7 @@ VALUES (
args := pgx.StrictNamedArgs{
"tenant_id": scope.GetTenantID(),
"policy_id": p.ID,
"document_id": p.ID,
"organization_id": p.OrganizationID,
"owner_id": p.OwnerID,
"title": p.Title,
@@ -182,44 +182,44 @@ VALUES (
return err
}
func (p Policy) Delete(
func (p Document) Delete(
ctx context.Context,
conn pg.Conn,
scope Scoper,
) error {
q := `
DELETE FROM policies WHERE %s AND id = @policy_id
DELETE FROM documents WHERE %s AND id = @document_id
`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{"policy_id": p.ID}
args := pgx.StrictNamedArgs{"document_id": p.ID}
maps.Copy(args, scope.SQLArguments())
_, err := conn.Exec(ctx, q, args)
return err
}
func (p *Policy) Update(
func (p *Document) Update(
ctx context.Context,
conn pg.Conn,
scope Scoper,
) error {
q := `
UPDATE
policies
documents
SET
title = @title,
current_published_version = @current_published_version,
owner_id = @owner_id,
updated_at = @updated_at
WHERE %s
AND id = @policy_id
AND id = @document_id
`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{
"policy_id": p.ID,
"document_id": p.ID,
"updated_at": time.Now(),
"title": p.Title,
"current_published_version": p.CurrentPublishedVersion,
@@ -229,18 +229,18 @@ WHERE %s
_, err := conn.Exec(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot update policy: %w", err)
return fmt.Errorf("cannot update document: %w", err)
}
return nil
}
func (p *Policies) LoadByControlID(
func (p *Documents) LoadByControlID(
ctx context.Context,
conn pg.Conn,
scope Scoper,
controlID gid.GID,
cursor *page.Cursor[PolicyOrderField],
cursor *page.Cursor[DocumentOrderField],
) error {
q := `
WITH plcs AS (
@@ -254,9 +254,9 @@ WITH plcs AS (
p.created_at,
p.updated_at
FROM
policies p
documents p
INNER JOIN
controls_policies cp ON p.id = cp.policy_id
controls_documents cp ON p.id = cp.document_id
WHERE
cp.control_id = @control_id
)
@@ -281,25 +281,25 @@ WHERE %s
rows, err := conn.Query(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot query policies: %w", err)
return fmt.Errorf("cannot query documents: %w", err)
}
policies, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[Policy])
documents, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[Document])
if err != nil {
return fmt.Errorf("cannot collect policies: %w", err)
return fmt.Errorf("cannot collect documents: %w", err)
}
*p = policies
*p = documents
return nil
}
func (p *Policies) LoadByRiskID(
func (p *Documents) LoadByRiskID(
ctx context.Context,
conn pg.Conn,
scope Scoper,
riskID gid.GID,
cursor *page.Cursor[PolicyOrderField],
cursor *page.Cursor[DocumentOrderField],
) error {
q := `
WITH plcs AS (
@@ -313,9 +313,9 @@ WITH plcs AS (
p.created_at,
p.updated_at
FROM
policies p
documents p
INNER JOIN
risks_policies rp ON p.id = rp.policy_id
risks_documents rp ON p.id = rp.document_id
WHERE
rp.risk_id = @risk_id
)
@@ -340,15 +340,15 @@ WHERE %s
rows, err := conn.Query(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot query policies: %w", err)
return fmt.Errorf("cannot query documents: %w", err)
}
policies, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[Policy])
documents, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[Document])
if err != nil {
return fmt.Errorf("cannot collect policies: %w", err)
return fmt.Errorf("cannot collect documents: %w", err)
}
*p = policies
*p = documents
return nil
}

View File

@@ -15,27 +15,27 @@
package coredata
type (
PolicyOrderField string
DocumentOrderField string
)
const (
PolicyOrderFieldCreatedAt PolicyOrderField = "CREATED_AT"
PolicyOrderFieldTitle PolicyOrderField = "TITLE"
DocumentOrderFieldCreatedAt DocumentOrderField = "CREATED_AT"
DocumentOrderFieldTitle DocumentOrderField = "TITLE"
)
func (p PolicyOrderField) Column() string {
func (p DocumentOrderField) Column() string {
return string(p)
}
func (p PolicyOrderField) String() string {
func (p DocumentOrderField) String() string {
return string(p)
}
func (p PolicyOrderField) MarshalText() ([]byte, error) {
func (p DocumentOrderField) MarshalText() ([]byte, error) {
return []byte(p.String()), nil
}
func (p *PolicyOrderField) UnmarshalText(text []byte) error {
*p = PolicyOrderField(text)
func (p *DocumentOrderField) UnmarshalText(text []byte) error {
*p = DocumentOrderField(text)
return nil
}

View File

@@ -20,55 +20,55 @@ import (
)
type (
PolicyStatus uint8
DocumentStatus uint8
)
const (
PolicyStatusDraft PolicyStatus = iota
PolicyStatusPublished
DocumentStatusDraft DocumentStatus = iota
DocumentStatusPublished
)
func (ps PolicyStatus) MarshalText() ([]byte, error) {
func (ps DocumentStatus) MarshalText() ([]byte, error) {
return []byte(ps.String()), nil
}
func (ps *PolicyStatus) UnmarshalText(data []byte) error {
func (ps *DocumentStatus) UnmarshalText(data []byte) error {
val := string(data)
switch val {
case PolicyStatusDraft.String():
*ps = PolicyStatusDraft
case PolicyStatusPublished.String():
*ps = PolicyStatusPublished
case DocumentStatusDraft.String():
*ps = DocumentStatusDraft
case DocumentStatusPublished.String():
*ps = DocumentStatusPublished
default:
return fmt.Errorf("invalid PolicyStatus value: %q", val)
return fmt.Errorf("invalid DocumentStatus value: %q", val)
}
return nil
}
func (ps PolicyStatus) String() string {
func (ps DocumentStatus) String() string {
var val string
switch ps {
case PolicyStatusDraft:
case DocumentStatusDraft:
val = "DRAFT"
case PolicyStatusPublished:
case DocumentStatusPublished:
val = "PUBLISHED"
}
return val
}
func (ps *PolicyStatus) Scan(value any) error {
func (ps *DocumentStatus) Scan(value any) error {
val, ok := value.(string)
if !ok {
return fmt.Errorf("invalid scan source for PolicyStatus, expected string got %T", value)
return fmt.Errorf("invalid scan source for DocumentStatus, expected string got %T", value)
}
return ps.UnmarshalText([]byte(val))
}
func (ps PolicyStatus) Value() (driver.Value, error) {
func (ps DocumentStatus) Value() (driver.Value, error) {
return ps.String(), nil
}

View File

@@ -27,34 +27,34 @@ import (
)
type (
PolicyVersion struct {
ID gid.GID `db:"id"`
PolicyID gid.GID `db:"policy_id"`
VersionNumber int `db:"version_number"`
Content string `db:"content"`
Changelog string `db:"changelog"`
CreatedBy gid.GID `db:"created_by"`
Status PolicyStatus `db:"status"`
PublishedBy *gid.GID `db:"published_by"`
PublishedAt *time.Time `db:"published_at"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
DocumentVersion struct {
ID gid.GID `db:"id"`
DocumentID gid.GID `db:"document_id"`
VersionNumber int `db:"version_number"`
Content string `db:"content"`
Changelog string `db:"changelog"`
CreatedBy gid.GID `db:"created_by"`
Status DocumentStatus `db:"status"`
PublishedBy *gid.GID `db:"published_by"`
PublishedAt *time.Time `db:"published_at"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
}
PolicyVersions []*PolicyVersion
DocumentVersions []*DocumentVersion
)
func (p *PolicyVersions) LoadByPolicyID(
func (p *DocumentVersions) LoadByDocumentID(
ctx context.Context,
conn pg.Conn,
scope Scoper,
policyID gid.GID,
cursor *page.Cursor[PolicyVersionOrderField],
documentID gid.GID,
cursor *page.Cursor[DocumentVersionOrderField],
) error {
q := `
SELECT
id,
policy_id,
document_id,
version_number,
content,
changelog,
@@ -65,53 +65,53 @@ SELECT
created_at,
updated_at
FROM
policy_versions
document_versions
WHERE
%s
AND policy_id = @policy_id
AND document_id = @document_id
AND %s
`
q = fmt.Sprintf(q, scope.SQLFragment(), cursor.SQLFragment())
args := pgx.StrictNamedArgs{"policy_id": policyID}
args := pgx.StrictNamedArgs{"document_id": documentID}
maps.Copy(args, scope.SQLArguments())
maps.Copy(args, cursor.SQLArguments())
rows, err := conn.Query(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot query policy versions: %w", err)
return fmt.Errorf("cannot query document versions: %w", err)
}
policyVersions, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[PolicyVersion])
documentVersions, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[DocumentVersion])
if err != nil {
return fmt.Errorf("cannot collect policy versions: %w", err)
return fmt.Errorf("cannot collect document versions: %w", err)
}
*p = policyVersions
*p = documentVersions
return nil
}
func (p PolicyVersion) CursorKey(orderBy PolicyVersionOrderField) page.CursorKey {
func (p DocumentVersion) CursorKey(orderBy DocumentVersionOrderField) page.CursorKey {
switch orderBy {
case PolicyVersionOrderFieldCreatedAt:
case DocumentVersionOrderFieldCreatedAt:
return page.NewCursorKey(p.ID, p.CreatedAt)
}
panic(fmt.Sprintf("unsupported order by: %s", orderBy))
}
func (p *PolicyVersion) LoadByID(
func (p *DocumentVersion) LoadByID(
ctx context.Context,
conn pg.Conn,
scope Scoper,
policyVersionID gid.GID,
documentVersionID gid.GID,
) error {
q := `
SELECT
id,
policy_id,
document_id,
version_number,
content,
changelog,
@@ -122,43 +122,43 @@ SELECT
created_at,
updated_at
FROM
policy_versions
document_versions
WHERE
%s
AND id = @policy_version_id
AND id = @document_version_id
LIMIT 1;
`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{"policy_version_id": policyVersionID}
args := pgx.StrictNamedArgs{"document_version_id": documentVersionID}
maps.Copy(args, scope.SQLArguments())
rows, err := conn.Query(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot query policy versions: %w", err)
return fmt.Errorf("cannot query document versions: %w", err)
}
policyVersion, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[PolicyVersion])
documentVersion, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[DocumentVersion])
if err != nil {
return fmt.Errorf("cannot collect policy version: %w", err)
return fmt.Errorf("cannot collect document version: %w", err)
}
*p = policyVersion
*p = documentVersion
return nil
}
func (p PolicyVersion) Insert(
func (p DocumentVersion) Insert(
ctx context.Context,
conn pg.Conn,
scope Scoper,
) error {
q := `
INSERT INTO policy_versions (
INSERT INTO document_versions (
tenant_id,
id,
policy_id,
document_id,
version_number,
content,
changelog,
@@ -169,7 +169,7 @@ INSERT INTO policy_versions (
) VALUES (
@tenant_id,
@id,
@policy_id,
@document_id,
@version_number,
@content,
@changelog,
@@ -184,7 +184,7 @@ INSERT INTO policy_versions (
args := pgx.StrictNamedArgs{
"tenant_id": scope.GetTenantID(),
"id": p.ID,
"policy_id": p.PolicyID,
"document_id": p.DocumentID,
"version_number": p.VersionNumber,
"content": p.Content,
"changelog": p.Changelog,
@@ -196,23 +196,23 @@ INSERT INTO policy_versions (
_, err := conn.Exec(ctx, q, args)
if err != nil {
return fmt.Errorf("error creating/updating policy version: %w", err)
return fmt.Errorf("error creating/updating document version: %w", err)
}
return nil
}
func (p *PolicyVersion) LoadByPolicyIDAndVersionNumber(
func (p *DocumentVersion) LoadByDocumentIDAndVersionNumber(
ctx context.Context,
conn pg.Conn,
scope Scoper,
policyID gid.GID,
documentID gid.GID,
versionNumber int,
) error {
q := `
SELECT
id,
policy_id,
document_id,
version_number,
content,
changelog,
@@ -223,10 +223,10 @@ SELECT
created_at,
updated_at
FROM
policy_versions
document_versions
WHERE
%s
AND policy_id = @policy_id
AND document_id = @document_id
AND version_number = @version_number
LIMIT 1;
`
@@ -234,7 +234,7 @@ LIMIT 1;
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{
"policy_id": policyID,
"document_id": documentID,
"version_number": versionNumber,
}
@@ -242,29 +242,29 @@ LIMIT 1;
rows, err := conn.Query(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot query policy versions: %w", err)
return fmt.Errorf("cannot query document versions: %w", err)
}
policyVersion, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[PolicyVersion])
documentVersion, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[DocumentVersion])
if err != nil {
return fmt.Errorf("cannot collect policy version: %w", err)
return fmt.Errorf("cannot collect document version: %w", err)
}
*p = policyVersion
*p = documentVersion
return nil
}
func (p *PolicyVersion) LoadLatestVersion(
func (p *DocumentVersion) LoadLatestVersion(
ctx context.Context,
conn pg.Conn,
scope Scoper,
policyID gid.GID,
documentID gid.GID,
) error {
q := `
SELECT
id,
policy_id,
document_id,
version_number,
content,
changelog,
@@ -275,10 +275,10 @@ SELECT
created_at,
updated_at
FROM
policy_versions
document_versions
WHERE
%s
AND policy_id = @policy_id
AND document_id = @document_id
ORDER BY created_at DESC
LIMIT 1;
`
@@ -286,58 +286,58 @@ LIMIT 1;
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{
"policy_id": policyID,
"document_id": documentID,
}
maps.Copy(args, scope.SQLArguments())
rows, err := conn.Query(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot query policy versions: %w", err)
return fmt.Errorf("cannot query document versions: %w", err)
}
policyVersion, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[PolicyVersion])
documentVersion, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[DocumentVersion])
if err != nil {
return fmt.Errorf("cannot collect policy version: %w", err)
return fmt.Errorf("cannot collect document version: %w", err)
}
*p = policyVersion
*p = documentVersion
return nil
}
func (p PolicyVersion) Update(
func (p DocumentVersion) Update(
ctx context.Context,
conn pg.Conn,
scope Scoper,
) error {
q := `
UPDATE policy_versions SET
UPDATE document_versions SET
changelog = @changelog,
status = @status,
status = @status,
content = @content,
published_by = @published_by,
published_at = @published_at,
updated_at = @updated_at
WHERE %s
AND id = @policy_version_id;`
AND id = @document_version_id;`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{
"policy_version_id": p.ID,
"changelog": p.Changelog,
"status": p.Status,
"content": p.Content,
"published_by": p.PublishedBy,
"published_at": p.PublishedAt,
"updated_at": p.UpdatedAt,
"document_version_id": p.ID,
"changelog": p.Changelog,
"status": p.Status,
"content": p.Content,
"published_by": p.PublishedBy,
"published_at": p.PublishedAt,
"updated_at": p.UpdatedAt,
}
maps.Copy(args, scope.SQLArguments())
_, err := conn.Exec(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot update policy version: %w", err)
return fmt.Errorf("cannot update document version: %w", err)
}
return nil

View File

@@ -15,27 +15,27 @@
package coredata
type (
PolicyVersionOrderField string
DocumentVersionOrderField string
)
const (
PolicyVersionOrderFieldCreatedAt PolicyVersionOrderField = "CREATED_AT"
PolicyVersionOrderFieldVersion PolicyVersionOrderField = "VERSION"
DocumentVersionOrderFieldCreatedAt DocumentVersionOrderField = "CREATED_AT"
DocumentVersionOrderFieldVersion DocumentVersionOrderField = "VERSION"
)
func (p PolicyVersionOrderField) Column() string {
func (p DocumentVersionOrderField) Column() string {
return string(p)
}
func (p PolicyVersionOrderField) String() string {
func (p DocumentVersionOrderField) String() string {
return string(p)
}
func (p PolicyVersionOrderField) MarshalText() ([]byte, error) {
func (p DocumentVersionOrderField) MarshalText() ([]byte, error) {
return []byte(p.String()), nil
}
func (p *PolicyVersionOrderField) UnmarshalText(text []byte) error {
*p = PolicyVersionOrderField(text)
func (p *DocumentVersionOrderField) UnmarshalText(text []byte) error {
*p = DocumentVersionOrderField(text)
return nil
}

View File

@@ -0,0 +1,281 @@
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
package coredata
import (
"context"
"fmt"
"maps"
"time"
"github.com/getprobo/probo/pkg/gid"
"github.com/getprobo/probo/pkg/page"
"github.com/jackc/pgx/v5"
"go.gearno.de/kit/pg"
)
type (
DocumentVersionSignature struct {
ID gid.GID `json:"id"`
DocumentVersionID gid.GID `json:"document_version_id"`
State DocumentVersionSignatureState `json:"state"`
SignedBy gid.GID `json:"signed_by"`
SignedAt *time.Time `json:"signed_at"`
RequestedAt time.Time `json:"requested_at"`
RequestedBy gid.GID `json:"requested_by"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
DocumentVersionSignatures []*DocumentVersionSignature
)
func (pvs DocumentVersionSignature) CursorKey(orderBy DocumentVersionSignatureOrderField) page.CursorKey {
switch orderBy {
case DocumentVersionSignatureOrderFieldCreatedAt:
return page.NewCursorKey(pvs.ID, pvs.CreatedAt)
case DocumentVersionSignatureOrderFieldSignedAt:
return page.NewCursorKey(pvs.ID, pvs.SignedAt)
}
panic(fmt.Sprintf("unsupported order by: %s", orderBy))
}
func (pvs *DocumentVersionSignature) LoadByDocumentVersionIDAndSignatory(
ctx context.Context,
conn pg.Conn,
scope Scoper,
documentVersionID gid.GID,
signatory gid.GID,
) error {
q := `
SELECT
id,
document_version_id,
state,
signed_by,
signed_at,
requested_at,
requested_by,
created_at,
updated_at
FROM
document_version_signatures
WHERE
%s
AND document_version_id = @document_version_id
AND signed_by = @signatory
LIMIT 1
`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{"document_version_id": documentVersionID, "signatory": signatory}
maps.Copy(args, scope.SQLArguments())
rows, err := conn.Query(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot query document version signature: %w", err)
}
documentVersionSignature, err := pgx.CollectOneRow(rows, pgx.RowToStructByName[DocumentVersionSignature])
if err != nil {
return fmt.Errorf("cannot collect document version signature: %w", err)
}
*pvs = documentVersionSignature
return nil
}
func (pvs *DocumentVersionSignature) LoadByID(
ctx context.Context,
conn pg.Conn,
scope Scoper,
signatureID gid.GID,
) error {
q := `
SELECT
id,
document_version_id,
state,
signed_by,
signed_at,
requested_at,
requested_by,
created_at,
updated_at
FROM
document_version_signatures
WHERE
id = @document_version_signature_id
AND %s
`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{"document_version_signature_id": signatureID}
maps.Copy(args, scope.SQLArguments())
rows, err := conn.Query(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot query document version signature: %w", err)
}
documentVersionSignature, err := pgx.CollectOneRow(rows, pgx.RowToStructByName[DocumentVersionSignature])
if err != nil {
return fmt.Errorf("cannot collect document version signature: %w", err)
}
*pvs = documentVersionSignature
return nil
}
func (pvs DocumentVersionSignature) Insert(
ctx context.Context,
conn pg.Conn,
scope Scoper,
) error {
q := `
INSERT INTO document_version_signatures (
id,
tenant_id,
document_version_id,
state,
signed_by,
signed_at,
requested_at,
requested_by,
created_at,
updated_at
) VALUES (
@id,
@tenant_id,
@document_version_id,
@state,
@signed_by,
@signed_at,
@requested_at,
@requested_by,
@created_at,
@updated_at
)
`
args := pgx.StrictNamedArgs{
"id": pvs.ID,
"tenant_id": scope.GetTenantID(),
"document_version_id": pvs.DocumentVersionID,
"state": pvs.State,
"signed_by": pvs.SignedBy,
"signed_at": pvs.SignedAt,
"requested_at": pvs.RequestedAt,
"requested_by": pvs.RequestedBy,
"created_at": pvs.CreatedAt,
"updated_at": pvs.UpdatedAt,
}
_, err := conn.Exec(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot insert document version signature: %w", err)
}
return nil
}
func (pvss *DocumentVersionSignatures) LoadByDocumentVersionID(
ctx context.Context,
conn pg.Conn,
scope Scoper,
documentVersionID gid.GID,
cursor *page.Cursor[DocumentVersionSignatureOrderField],
) error {
q := `
SELECT
id,
document_version_id,
state,
signed_by,
signed_at,
requested_at,
requested_by,
created_at,
updated_at
FROM
document_version_signatures
WHERE
%s
AND document_version_id = @document_version_id
AND %s
`
q = fmt.Sprintf(q, scope.SQLFragment(), cursor.SQLFragment())
args := pgx.StrictNamedArgs{"document_version_id": documentVersionID}
maps.Copy(args, scope.SQLArguments())
maps.Copy(args, cursor.SQLArguments())
rows, err := conn.Query(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot query document version signatures: %w", err)
}
documentVersionSignatures, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[DocumentVersionSignature])
if err != nil {
return fmt.Errorf("cannot collect document version signatures: %w", err)
}
*pvss = documentVersionSignatures
return nil
}
func (pvs *DocumentVersionSignature) Update(
ctx context.Context,
conn pg.Conn,
scope Scoper,
) error {
q := `
UPDATE document_version_signatures
SET
state = @state,
signed_by = @signed_by,
signed_at = @signed_at,
updated_at = @updated_at
WHERE
%s
AND id = @id
`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{
"id": pvs.ID,
"state": pvs.State,
"signed_by": pvs.SignedBy,
"signed_at": pvs.SignedAt,
"updated_at": pvs.UpdatedAt,
}
maps.Copy(args, scope.SQLArguments())
_, err := conn.Exec(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot update document version signature: %w", err)
}
return nil
}

View File

@@ -15,27 +15,27 @@
package coredata
type (
PolicyVersionSignatureOrderField string
DocumentVersionSignatureOrderField string
)
const (
PolicyVersionSignatureOrderFieldCreatedAt PolicyVersionSignatureOrderField = "CREATED_AT"
PolicyVersionSignatureOrderFieldSignedAt PolicyVersionSignatureOrderField = "SIGNED_AT"
DocumentVersionSignatureOrderFieldCreatedAt DocumentVersionSignatureOrderField = "CREATED_AT"
DocumentVersionSignatureOrderFieldSignedAt DocumentVersionSignatureOrderField = "SIGNED_AT"
)
func (p PolicyVersionSignatureOrderField) Column() string {
func (p DocumentVersionSignatureOrderField) Column() string {
return string(p)
}
func (p PolicyVersionSignatureOrderField) String() string {
func (p DocumentVersionSignatureOrderField) String() string {
return string(p)
}
func (p PolicyVersionSignatureOrderField) MarshalText() ([]byte, error) {
func (p DocumentVersionSignatureOrderField) MarshalText() ([]byte, error) {
return []byte(p.String()), nil
}
func (p *PolicyVersionSignatureOrderField) UnmarshalText(text []byte) error {
*p = PolicyVersionSignatureOrderField(text)
func (p *DocumentVersionSignatureOrderField) UnmarshalText(text []byte) error {
*p = DocumentVersionSignatureOrderField(text)
return nil
}

View File

@@ -20,57 +20,57 @@ import (
)
type (
PolicyVersionSignatureState string
DocumentVersionSignatureState string
)
const (
PolicyVersionSignatureStateRequested PolicyVersionSignatureState = "REQUESTED"
PolicyVersionSignatureStateSigned PolicyVersionSignatureState = "SIGNED"
DocumentVersionSignatureStateRequested DocumentVersionSignatureState = "REQUESTED"
DocumentVersionSignatureStateSigned DocumentVersionSignatureState = "SIGNED"
)
func (pvs PolicyVersionSignatureState) MarshalText() ([]byte, error) {
func (pvs DocumentVersionSignatureState) MarshalText() ([]byte, error) {
return []byte(pvs.String()), nil
}
func (pvs *PolicyVersionSignatureState) UnmarshalText(data []byte) error {
func (pvs *DocumentVersionSignatureState) UnmarshalText(data []byte) error {
val := string(data)
switch val {
case PolicyVersionSignatureStateRequested.String():
*pvs = PolicyVersionSignatureStateRequested
case PolicyVersionSignatureStateSigned.String():
*pvs = PolicyVersionSignatureStateSigned
case DocumentVersionSignatureStateRequested.String():
*pvs = DocumentVersionSignatureStateRequested
case DocumentVersionSignatureStateSigned.String():
*pvs = DocumentVersionSignatureStateSigned
default:
return fmt.Errorf("invalid PolicyVersionSignatureState value: %q", val)
return fmt.Errorf("invalid DocumentVersionSignatureState value: %q", val)
}
return nil
}
func (pvs PolicyVersionSignatureState) String() string {
func (pvs DocumentVersionSignatureState) String() string {
var val string
switch pvs {
case PolicyVersionSignatureStateRequested:
case DocumentVersionSignatureStateRequested:
val = "REQUESTED"
case PolicyVersionSignatureStateSigned:
case DocumentVersionSignatureStateSigned:
val = "SIGNED"
default:
panic(fmt.Errorf("invalid PolicyVersionSignatureState value: %q", string(pvs)))
panic(fmt.Errorf("invalid DocumentVersionSignatureState value: %q", string(pvs)))
}
return val
}
func (pvs *PolicyVersionSignatureState) Scan(value any) error {
func (pvs *DocumentVersionSignatureState) Scan(value any) error {
val, ok := value.(string)
if !ok {
return fmt.Errorf("invalid scan source for PolicyVersionSignatureState, expected string got %T", value)
return fmt.Errorf("invalid scan source for DocumentVersionSignatureState, expected string got %T", value)
}
return pvs.UnmarshalText([]byte(val))
}
func (pvs PolicyVersionSignatureState) Value() (driver.Value, error) {
func (pvs DocumentVersionSignatureState) Value() (driver.Value, error) {
return pvs.String(), nil
}

View File

@@ -25,12 +25,12 @@ const (
VendorEntityType
PeopleEntityType
VendorComplianceReportEntityType
PolicyEntityType
DocumentEntityType
UserEntityType
SessionEntityType
EmailEntityType
ControlEntityType
RiskEntityType
PolicyVersionEntityType
PolicyVersionSignatureEntityType
DocumentVersionEntityType
DocumentVersionSignatureEntityType
)

View File

@@ -0,0 +1,36 @@
-- Rename policies table to documents
ALTER TABLE policies RENAME TO documents;
-- Rename risks_policies table to risks_documents
ALTER TABLE risks_policies RENAME TO risks_documents;
-- Update the foreign key reference in risks_documents
ALTER TABLE risks_documents RENAME CONSTRAINT risks_policies_policy_id_fkey TO risks_documents_document_id_fkey;
-- Rename the policy_id column in risks_documents to document_id
ALTER TABLE risks_documents RENAME COLUMN policy_id TO document_id;
-- Rename controls_policies table to controls_documents
ALTER TABLE controls_policies RENAME TO controls_documents;
-- Update controls_documents foreign key and column
ALTER TABLE controls_documents RENAME COLUMN policy_id TO document_id;
ALTER TABLE controls_documents RENAME CONSTRAINT controls_policies_policy_id_fkey TO controls_documents_document_id_fkey;
-- Rename policy_versions table to document_versions
ALTER TABLE policy_versions RENAME TO document_versions;
-- Update document_versions foreign key and column
ALTER TABLE document_versions RENAME COLUMN policy_id TO document_id;
ALTER TABLE document_versions RENAME CONSTRAINT policy_versions_policy_id_fkey TO document_versions_document_id_fkey;
-- Rename policy_version_signatures table to document_version_signatures
ALTER TABLE policy_version_signatures RENAME TO document_version_signatures;
-- Update document_version_signatures foreign key and column
ALTER TABLE document_version_signatures RENAME COLUMN policy_version_id TO document_version_id;
ALTER TABLE document_version_signatures RENAME CONSTRAINT policy_version_signatures_policy_version_id_fkey TO document_version_signatures_document_version_id_fkey;
-- Rename the unique index, preserving the WHERE clause
DROP INDEX policy_one_draft_version_idx;
CREATE UNIQUE INDEX document_one_draft_version_idx ON document_versions (document_id, status) WHERE status = 'DRAFT';

View File

@@ -1,281 +0,0 @@
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
package coredata
import (
"context"
"fmt"
"maps"
"time"
"github.com/getprobo/probo/pkg/gid"
"github.com/getprobo/probo/pkg/page"
"github.com/jackc/pgx/v5"
"go.gearno.de/kit/pg"
)
type (
PolicyVersionSignature struct {
ID gid.GID `json:"id"`
PolicyVersionID gid.GID `json:"policy_version_id"`
State PolicyVersionSignatureState `json:"state"`
SignedBy gid.GID `json:"signed_by"`
SignedAt *time.Time `json:"signed_at"`
RequestedAt time.Time `json:"requested_at"`
RequestedBy gid.GID `json:"requested_by"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
PolicyVersionSignatures []*PolicyVersionSignature
)
func (pvs PolicyVersionSignature) CursorKey(orderBy PolicyVersionSignatureOrderField) page.CursorKey {
switch orderBy {
case PolicyVersionSignatureOrderFieldCreatedAt:
return page.NewCursorKey(pvs.ID, pvs.CreatedAt)
case PolicyVersionSignatureOrderFieldSignedAt:
return page.NewCursorKey(pvs.ID, pvs.SignedAt)
}
panic(fmt.Sprintf("unsupported order by: %s", orderBy))
}
func (pvs *PolicyVersionSignature) LoadByPolicyVersionIDAndSignatory(
ctx context.Context,
conn pg.Conn,
scope Scoper,
policyVersionID gid.GID,
signatory gid.GID,
) error {
q := `
SELECT
id,
policy_version_id,
state,
signed_by,
signed_at,
requested_at,
requested_by,
created_at,
updated_at
FROM
policy_version_signatures
WHERE
%s
AND policy_version_id = @policy_version_id
AND signed_by = @signatory
LIMIT 1
`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{"policy_version_id": policyVersionID, "signatory": signatory}
maps.Copy(args, scope.SQLArguments())
rows, err := conn.Query(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot query policy version signature: %w", err)
}
policyVersionSignature, err := pgx.CollectOneRow(rows, pgx.RowToStructByName[PolicyVersionSignature])
if err != nil {
return fmt.Errorf("cannot collect policy version signature: %w", err)
}
*pvs = policyVersionSignature
return nil
}
func (pvs *PolicyVersionSignature) LoadByID(
ctx context.Context,
conn pg.Conn,
scope Scoper,
signatureID gid.GID,
) error {
q := `
SELECT
id,
policy_version_id,
state,
signed_by,
signed_at,
requested_at,
requested_by,
created_at,
updated_at
FROM
policy_version_signatures
WHERE
id = @policy_version_signature_id
AND %s
`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{"policy_version_signature_id": signatureID}
maps.Copy(args, scope.SQLArguments())
rows, err := conn.Query(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot query policy version signature: %w", err)
}
policyVersionSignature, err := pgx.CollectOneRow(rows, pgx.RowToStructByName[PolicyVersionSignature])
if err != nil {
return fmt.Errorf("cannot collect policy version signature: %w", err)
}
*pvs = policyVersionSignature
return nil
}
func (pvs PolicyVersionSignature) Insert(
ctx context.Context,
conn pg.Conn,
scope Scoper,
) error {
q := `
INSERT INTO policy_version_signatures (
id,
tenant_id,
policy_version_id,
state,
signed_by,
signed_at,
requested_at,
requested_by,
created_at,
updated_at
) VALUES (
@id,
@tenant_id,
@policy_version_id,
@state,
@signed_by,
@signed_at,
@requested_at,
@requested_by,
@created_at,
@updated_at
)
`
args := pgx.StrictNamedArgs{
"id": pvs.ID,
"tenant_id": scope.GetTenantID(),
"policy_version_id": pvs.PolicyVersionID,
"state": pvs.State,
"signed_by": pvs.SignedBy,
"signed_at": pvs.SignedAt,
"requested_at": pvs.RequestedAt,
"requested_by": pvs.RequestedBy,
"created_at": pvs.CreatedAt,
"updated_at": pvs.UpdatedAt,
}
_, err := conn.Exec(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot insert policy version signature: %w", err)
}
return nil
}
func (pvss *PolicyVersionSignatures) LoadByPolicyVersionID(
ctx context.Context,
conn pg.Conn,
scope Scoper,
policyVersionID gid.GID,
cursor *page.Cursor[PolicyVersionSignatureOrderField],
) error {
q := `
SELECT
id,
policy_version_id,
state,
signed_by,
signed_at,
requested_at,
requested_by,
created_at,
updated_at
FROM
policy_version_signatures
WHERE
%s
AND policy_version_id = @policy_version_id
AND %s
`
q = fmt.Sprintf(q, scope.SQLFragment(), cursor.SQLFragment())
args := pgx.StrictNamedArgs{"policy_version_id": policyVersionID}
maps.Copy(args, scope.SQLArguments())
maps.Copy(args, cursor.SQLArguments())
rows, err := conn.Query(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot query policy version signatures: %w", err)
}
policyVersionSignatures, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[PolicyVersionSignature])
if err != nil {
return fmt.Errorf("cannot collect policy version signatures: %w", err)
}
*pvss = policyVersionSignatures
return nil
}
func (pvs *PolicyVersionSignature) Update(
ctx context.Context,
conn pg.Conn,
scope Scoper,
) error {
q := `
UPDATE policy_version_signatures
SET
state = @state,
signed_by = @signed_by,
signed_at = @signed_at,
updated_at = @updated_at
WHERE
%s
AND id = @id
`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{
"id": pvs.ID,
"state": pvs.State,
"signed_by": pvs.SignedBy,
"signed_at": pvs.SignedAt,
"updated_at": pvs.UpdatedAt,
}
maps.Copy(args, scope.SQLArguments())
_, err := conn.Exec(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot update policy version signature: %w", err)
}
return nil
}

View File

@@ -26,69 +26,69 @@ import (
)
type (
RiskPolicy struct {
RiskID gid.GID `db:"risk_id"`
PolicyID gid.GID `db:"policy_id"`
TenantID gid.TenantID `db:"tenant_id"`
CreatedAt time.Time `db:"created_at"`
RiskDocument struct {
RiskID gid.GID `db:"risk_id"`
DocumentID gid.GID `db:"document_id"`
TenantID gid.TenantID `db:"tenant_id"`
CreatedAt time.Time `db:"created_at"`
}
RiskPolicies []*RiskPolicy
RiskDocuments []*RiskDocument
)
func (rp RiskPolicy) Insert(
func (rp RiskDocument) Insert(
ctx context.Context,
conn pg.Conn,
scope Scoper,
) error {
q := `
INSERT INTO
risks_policies (
risks_documents (
risk_id,
policy_id,
document_id,
tenant_id,
created_at
)
VALUES (
@risk_id,
@policy_id,
@document_id,
@tenant_id,
@created_at
);
`
args := pgx.StrictNamedArgs{
"risk_id": rp.RiskID,
"policy_id": rp.PolicyID,
"tenant_id": scope.GetTenantID(),
"created_at": rp.CreatedAt,
"risk_id": rp.RiskID,
"document_id": rp.DocumentID,
"tenant_id": scope.GetTenantID(),
"created_at": rp.CreatedAt,
}
_, err := conn.Exec(ctx, q, args)
return err
}
func (rp RiskPolicy) Delete(
func (rp RiskDocument) Delete(
ctx context.Context,
conn pg.Conn,
scope Scoper,
riskID gid.GID,
policyID gid.GID,
documentID gid.GID,
) error {
q := `
DELETE
FROM
risks_policies
risks_documents
WHERE
%s
AND risk_id = @risk_id
AND policy_id = @policy_id;
AND document_id = @document_id;
`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{
"risk_id": riskID,
"policy_id": policyID,
"risk_id": riskID,
"document_id": documentID,
}
maps.Copy(args, scope.SQLArguments())

View File

@@ -55,22 +55,22 @@ type (
}
)
func (s ControlService) ListForPolicyID(
func (s ControlService) ListForDocumentID(
ctx context.Context,
policyID gid.GID,
documentID gid.GID,
cursor *page.Cursor[coredata.ControlOrderField],
) (*page.Page[*coredata.Control, coredata.ControlOrderField], error) {
var controls coredata.Controls
policy := &coredata.Policy{}
document := &coredata.Document{}
err := s.svc.pg.WithConn(
ctx,
func(conn pg.Conn) error {
if err := policy.LoadByID(ctx, conn, s.svc.scope, policyID); err != nil {
return fmt.Errorf("cannot load policy: %w", err)
if err := document.LoadByID(ctx, conn, s.svc.scope, documentID); err != nil {
return fmt.Errorf("cannot load document: %w", err)
}
return controls.LoadByPolicyID(ctx, conn, s.svc.scope, policyID, cursor)
return controls.LoadByDocumentID(ctx, conn, s.svc.scope, documentID, cursor)
},
)
@@ -179,13 +179,13 @@ func (s ControlService) DeleteMeasureMapping(
return control, measure, nil
}
func (s ControlService) CreatePolicyMapping(
func (s ControlService) CreateDocumentMapping(
ctx context.Context,
controlID gid.GID,
policyID gid.GID,
) (*coredata.Control, *coredata.Policy, error) {
documentID gid.GID,
) (*coredata.Control, *coredata.Document, error) {
control := &coredata.Control{}
policy := &coredata.Policy{}
document := &coredata.Document{}
err := s.svc.pg.WithConn(
ctx,
@@ -194,19 +194,19 @@ func (s ControlService) CreatePolicyMapping(
return fmt.Errorf("cannot load control: %w", err)
}
if err := policy.LoadByID(ctx, conn, s.svc.scope, policyID); err != nil {
return fmt.Errorf("cannot load policy: %w", err)
if err := document.LoadByID(ctx, conn, s.svc.scope, documentID); err != nil {
return fmt.Errorf("cannot load document: %w", err)
}
controlPolicy := &coredata.ControlPolicy{
ControlID: control.ID,
PolicyID: policy.ID,
TenantID: s.svc.scope.GetTenantID(),
CreatedAt: time.Now(),
controlDocument := &coredata.ControlDocument{
ControlID: control.ID,
DocumentID: document.ID,
TenantID: s.svc.scope.GetTenantID(),
CreatedAt: time.Now(),
}
if err := controlPolicy.Insert(ctx, conn, s.svc.scope); err != nil {
return fmt.Errorf("cannot insert control policy: %w", err)
if err := controlDocument.Insert(ctx, conn, s.svc.scope); err != nil {
return fmt.Errorf("cannot insert control document: %w", err)
}
return nil
@@ -214,19 +214,19 @@ func (s ControlService) CreatePolicyMapping(
)
if err != nil {
return nil, nil, fmt.Errorf("cannot create control policy mapping: %w", err)
return nil, nil, fmt.Errorf("cannot create control document mapping: %w", err)
}
return control, policy, nil
return control, document, nil
}
func (s ControlService) DeletePolicyMapping(
func (s ControlService) DeleteDocumentMapping(
ctx context.Context,
controlID gid.GID,
policyID gid.GID,
) (*coredata.Control, *coredata.Policy, error) {
documentID gid.GID,
) (*coredata.Control, *coredata.Document, error) {
control := &coredata.Control{}
policy := &coredata.Policy{}
document := &coredata.Document{}
err := s.svc.pg.WithConn(
ctx,
@@ -235,13 +235,13 @@ func (s ControlService) DeletePolicyMapping(
return fmt.Errorf("cannot load control: %w", err)
}
if err := policy.LoadByID(ctx, conn, s.svc.scope, policyID); err != nil {
return fmt.Errorf("cannot load policy: %w", err)
if err := document.LoadByID(ctx, conn, s.svc.scope, documentID); err != nil {
return fmt.Errorf("cannot load document: %w", err)
}
controlPolicy := &coredata.ControlPolicy{}
if err := controlPolicy.Delete(ctx, conn, s.svc.scope, control.ID, policy.ID); err != nil {
return fmt.Errorf("cannot delete control policy mapping: %w", err)
controlDocument := &coredata.ControlDocument{}
if err := controlDocument.Delete(ctx, conn, s.svc.scope, control.ID, document.ID); err != nil {
return fmt.Errorf("cannot delete control document mapping: %w", err)
}
return nil
@@ -249,10 +249,10 @@ func (s ControlService) DeletePolicyMapping(
)
if err != nil {
return nil, nil, fmt.Errorf("cannot delete control policy mapping: %w", err)
return nil, nil, fmt.Errorf("cannot delete control document mapping: %w", err)
}
return control, policy, nil
return control, document, nil
}
func (s ControlService) Create(

View File

@@ -0,0 +1,634 @@
package probo
import (
"context"
"fmt"
"net/url"
"time"
"github.com/getprobo/probo/pkg/coredata"
"github.com/getprobo/probo/pkg/gid"
"github.com/getprobo/probo/pkg/page"
"github.com/getprobo/probo/pkg/statelesstoken"
"github.com/jackc/pgx/v5"
"go.gearno.de/kit/pg"
)
type (
DocumentService struct {
svc *TenantService
}
CreateDocumentRequest struct {
OrganizationID gid.GID
Title string
Content string
OwnerID gid.GID
CreatedBy gid.GID
}
UpdateDocumentVersionRequest struct {
ID gid.GID
Content string
}
RequestSignatureRequest struct {
DocumentVersionID gid.GID
RequestedBy gid.GID
Signatory gid.GID
}
SigningRequestData struct {
OrganizationID gid.GID `json:"organization_id"`
PeopleID gid.GID `json:"people_id"`
}
)
const (
TokenTypeSigningRequest = "signing_request"
)
func (s *DocumentService) Get(
ctx context.Context,
documentID gid.GID,
) (*coredata.Document, error) {
document := &coredata.Document{}
err := s.svc.pg.WithConn(
ctx,
func(conn pg.Conn) error {
return document.LoadByID(ctx, conn, s.svc.scope, documentID)
},
)
if err != nil {
return nil, err
}
return document, nil
}
func (s *DocumentService) PublishVersion(
ctx context.Context,
documentID gid.GID,
publishedBy gid.GID,
) (*coredata.Document, *coredata.DocumentVersion, error) {
document := &coredata.Document{}
documentVersion := &coredata.DocumentVersion{}
now := time.Now()
err := s.svc.pg.WithTx(
ctx,
func(tx pg.Conn) error {
if err := document.LoadByID(ctx, tx, s.svc.scope, documentID); err != nil {
return fmt.Errorf("cannot load document %q: %w", documentID, err)
}
if err := documentVersion.LoadLatestVersion(ctx, tx, s.svc.scope, documentID); err != nil {
return fmt.Errorf("cannot load current draft: %w", err)
}
if documentVersion.Status != coredata.DocumentStatusDraft {
return fmt.Errorf("cannot publish version")
}
document.CurrentPublishedVersion = &documentVersion.VersionNumber
document.UpdatedAt = now
documentVersion.Status = coredata.DocumentStatusPublished
documentVersion.PublishedAt = &now
documentVersion.PublishedBy = &publishedBy
documentVersion.UpdatedAt = now
if err := document.Update(ctx, tx, s.svc.scope); err != nil {
return fmt.Errorf("cannot update document: %w", err)
}
if err := documentVersion.Update(ctx, tx, s.svc.scope); err != nil {
return fmt.Errorf("cannot update document version: %w", err)
}
return nil
},
)
if err != nil {
return nil, nil, err
}
return document, documentVersion, nil
}
func (s *DocumentService) Create(
ctx context.Context,
req CreateDocumentRequest,
) (*coredata.Document, *coredata.DocumentVersion, error) {
now := time.Now()
documentID := gid.New(s.svc.scope.GetTenantID(), coredata.DocumentEntityType)
documentVersionID := gid.New(s.svc.scope.GetTenantID(), coredata.DocumentVersionEntityType)
organization := &coredata.Organization{}
people := &coredata.People{}
document := &coredata.Document{
ID: documentID,
Title: req.Title,
CreatedAt: now,
UpdatedAt: now,
}
documentVersion := &coredata.DocumentVersion{
ID: documentVersionID,
DocumentID: documentID,
VersionNumber: 1,
Content: req.Content,
Status: coredata.DocumentStatusDraft,
CreatedBy: req.CreatedBy,
CreatedAt: now,
UpdatedAt: now,
}
err := s.svc.pg.WithTx(
ctx,
func(conn pg.Conn) error {
if err := organization.LoadByID(ctx, conn, s.svc.scope, req.OrganizationID); err != nil {
return fmt.Errorf("cannot load organization: %w", err)
}
if err := people.LoadByID(ctx, conn, s.svc.scope, req.OwnerID); err != nil {
return fmt.Errorf("cannot load people: %w", err)
}
document.OrganizationID = organization.ID
document.OwnerID = people.ID
if err := document.Insert(ctx, conn, s.svc.scope); err != nil {
return fmt.Errorf("cannot insert document: %w", err)
}
if err := documentVersion.Insert(ctx, conn, s.svc.scope); err != nil {
return fmt.Errorf("cannot create document version: %w", err)
}
return nil
},
)
if err != nil {
return nil, nil, err
}
return document, documentVersion, nil
}
func (s *DocumentService) ListSigningRequests(
ctx context.Context,
organizationID gid.GID,
peopleID gid.GID,
) ([]map[string]any, error) {
q := `
SELECT
p.title,
pv.content,
pv.id AS document_version_id
FROM
documents p
INNER JOIN document_versions pv ON pv.document_id = p.id
INNER JOIN document_version_signatures pvs ON pvs.document_version_id = pv.id
WHERE
p.tenant_id = $1
AND pvs.signed_by = $2
AND pvs.signed_at IS NULL
`
var results []map[string]any
err := s.svc.pg.WithConn(
ctx,
func(conn pg.Conn) error {
rows, err := conn.Query(ctx, q, s.svc.scope.GetTenantID(), peopleID)
if err != nil {
return fmt.Errorf("cannot query documents: %w", err)
}
results, err = pgx.CollectRows(rows, pgx.RowToMap)
if err != nil {
return err
}
return nil
},
)
if err != nil {
return nil, err
}
return results, nil
}
func (s *DocumentService) SendSigningNotifications(
ctx context.Context,
organizationID gid.GID,
) error {
err := s.svc.pg.WithTx(
ctx,
func(tx pg.Conn) error {
var peoples coredata.Peoples
if err := peoples.LoadAwaitingSigning(ctx, tx, s.svc.scope); err != nil {
return fmt.Errorf("cannot load people: %w", err)
}
for _, people := range peoples {
now := time.Now()
emailID := gid.New(s.svc.scope.GetTenantID(), coredata.EmailEntityType)
token, err := statelesstoken.NewToken(
s.svc.tokenSecret,
TokenTypeSigningRequest,
time.Hour*24*7,
SigningRequestData{
OrganizationID: organizationID,
PeopleID: people.ID,
},
)
if err != nil {
return fmt.Errorf("cannot create signing request token: %w", err)
}
signRequestURL := url.URL{
Scheme: "https",
Host: s.svc.hostname,
Path: "/documents/signing-requests",
RawQuery: url.Values{
"token": []string{token},
}.Encode(),
}
email := &coredata.Email{
ID: emailID,
RecipientEmail: people.PrimaryEmailAddress,
RecipientName: people.FullName,
Subject: "Probo - Documents Signing Request",
TextBody: fmt.Sprintf("Hi,\nYou have documents awaiting your signature. Please follow this link to sign them: %s", signRequestURL.String()),
CreatedAt: now,
UpdatedAt: now,
}
if err := email.Insert(ctx, tx); err != nil {
return fmt.Errorf("cannot insert email: %w", err)
}
}
return nil
},
)
if err != nil {
return fmt.Errorf("cannot send signing notifications: %w", err)
}
return nil
}
func (s *DocumentService) SignDocumentVersion(
ctx context.Context,
documentVersionID gid.GID,
signatory gid.GID,
) error {
documentVersion := &coredata.DocumentVersion{}
documentVersionSignature := &coredata.DocumentVersionSignature{}
now := time.Now()
err := s.svc.pg.WithTx(
ctx,
func(conn pg.Conn) error {
if err := documentVersion.LoadByID(ctx, conn, s.svc.scope, documentVersionID); err != nil {
return fmt.Errorf("cannot load document version %q: %w", documentVersionID, err)
}
if documentVersion.Status != coredata.DocumentStatusPublished {
return fmt.Errorf("cannot sign unpublished version")
}
if err := documentVersionSignature.LoadByDocumentVersionIDAndSignatory(ctx, conn, s.svc.scope, documentVersionID, signatory); err != nil {
return fmt.Errorf("cannot load document version signature: %w", err)
}
if documentVersionSignature.State == coredata.DocumentVersionSignatureStateSigned {
return fmt.Errorf("document version already signed")
}
documentVersionSignature.State = coredata.DocumentVersionSignatureStateSigned
documentVersionSignature.SignedAt = &now
documentVersionSignature.UpdatedAt = now
if err := documentVersion.Update(ctx, conn, s.svc.scope); err != nil {
return fmt.Errorf("cannot update document version: %w", err)
}
if err := documentVersionSignature.Update(ctx, conn, s.svc.scope); err != nil {
return fmt.Errorf("cannot update document version signature: %w", err)
}
return nil
},
)
if err != nil {
return fmt.Errorf("cannot sign document version: %w", err)
}
return nil
}
func (s *DocumentService) UpdateVersion(
ctx context.Context,
req UpdateDocumentVersionRequest,
) (*coredata.DocumentVersion, error) {
documentVersion := &coredata.DocumentVersion{}
err := s.svc.pg.WithTx(
ctx,
func(conn pg.Conn) error {
if err := documentVersion.LoadByID(ctx, conn, s.svc.scope, req.ID); err != nil {
return fmt.Errorf("cannot load document version %q: %w", req.ID, err)
}
if documentVersion.Status != coredata.DocumentStatusDraft {
return fmt.Errorf("cannot update published version")
}
documentVersion.Content = req.Content
documentVersion.UpdatedAt = time.Now()
if err := documentVersion.Update(ctx, conn, s.svc.scope); err != nil {
return fmt.Errorf("cannot update document version: %w", err)
}
return nil
},
)
if err != nil {
return nil, err
}
return documentVersion, nil
}
func (s *DocumentService) GetVersionSignature(
ctx context.Context,
signatureID gid.GID,
) (*coredata.DocumentVersionSignature, error) {
documentVersionSignature := &coredata.DocumentVersionSignature{}
err := s.svc.pg.WithConn(
ctx,
func(conn pg.Conn) error {
return documentVersionSignature.LoadByID(ctx, conn, s.svc.scope, signatureID)
},
)
if err != nil {
return nil, err
}
return documentVersionSignature, nil
}
func (s *DocumentService) RequestSignature(
ctx context.Context,
req RequestSignatureRequest,
) (*coredata.DocumentVersionSignature, error) {
documentVersionSignatureID := gid.New(s.svc.scope.GetTenantID(), coredata.DocumentVersionSignatureEntityType)
documentVersion, err := s.GetVersion(ctx, req.DocumentVersionID)
if err != nil {
return nil, fmt.Errorf("cannot get document version: %w", err)
}
if documentVersion.Status != coredata.DocumentStatusPublished {
return nil, fmt.Errorf("cannot request signature for unpublished version")
}
now := time.Now()
documentVersionSignature := &coredata.DocumentVersionSignature{
ID: documentVersionSignatureID,
DocumentVersionID: req.DocumentVersionID,
State: coredata.DocumentVersionSignatureStateRequested,
RequestedBy: req.RequestedBy,
RequestedAt: now,
SignedBy: req.Signatory,
SignedAt: nil,
CreatedAt: now,
UpdatedAt: now,
}
err = s.svc.pg.WithTx(
ctx,
func(conn pg.Conn) error {
if err := documentVersionSignature.Insert(ctx, conn, s.svc.scope); err != nil {
return fmt.Errorf("cannot insert document version signature: %w", err)
}
return nil
},
)
if err != nil {
return nil, err
}
return documentVersionSignature, nil
}
func (s *DocumentService) ListSignatures(
ctx context.Context,
documentVersionID gid.GID,
cursor *page.Cursor[coredata.DocumentVersionSignatureOrderField],
) (*page.Page[*coredata.DocumentVersionSignature, coredata.DocumentVersionSignatureOrderField], error) {
var documentVersionSignatures coredata.DocumentVersionSignatures
err := s.svc.pg.WithConn(
ctx,
func(conn pg.Conn) error {
return documentVersionSignatures.LoadByDocumentVersionID(ctx, conn, s.svc.scope, documentVersionID, cursor)
},
)
if err != nil {
return nil, err
}
return page.NewPage(documentVersionSignatures, cursor), nil
}
func (s *DocumentService) CreateDraft(
ctx context.Context,
documentID gid.GID,
createdBy gid.GID,
) (*coredata.DocumentVersion, error) {
draftVersionID := gid.New(s.svc.scope.GetTenantID(), coredata.DocumentVersionEntityType)
latestVersion := &coredata.DocumentVersion{}
draftVersion := &coredata.DocumentVersion{}
now := time.Now()
err := s.svc.pg.WithTx(
ctx,
func(conn pg.Conn) error {
if err := latestVersion.LoadLatestVersion(ctx, conn, s.svc.scope, documentID); err != nil {
return fmt.Errorf("cannot load latest version: %w", err)
}
if latestVersion.Status != coredata.DocumentStatusPublished {
return fmt.Errorf("cannot create draft from unpublished version")
}
draftVersion.ID = draftVersionID
draftVersion.DocumentID = documentID
draftVersion.VersionNumber = latestVersion.VersionNumber + 1
draftVersion.Content = latestVersion.Content
draftVersion.Status = coredata.DocumentStatusDraft
draftVersion.CreatedBy = createdBy
draftVersion.CreatedAt = now
draftVersion.UpdatedAt = now
if err := draftVersion.Insert(ctx, conn, s.svc.scope); err != nil {
return fmt.Errorf("cannot create draft: %w", err)
}
return nil
},
)
if err != nil {
return nil, err
}
return draftVersion, nil
}
func (s *DocumentService) Delete(
ctx context.Context,
documentID gid.GID,
) error {
document := coredata.Document{ID: documentID}
return s.svc.pg.WithConn(
ctx,
func(conn pg.Conn) error {
return document.Delete(ctx, conn, s.svc.scope)
},
)
}
func (s *DocumentService) ListVersions(
ctx context.Context,
documentID gid.GID,
cursor *page.Cursor[coredata.DocumentVersionOrderField],
) (*page.Page[*coredata.DocumentVersion, coredata.DocumentVersionOrderField], error) {
var documentVersions coredata.DocumentVersions
err := s.svc.pg.WithConn(
ctx,
func(conn pg.Conn) error {
return documentVersions.LoadByDocumentID(ctx, conn, s.svc.scope, documentID, cursor)
},
)
if err != nil {
return nil, err
}
return page.NewPage(documentVersions, cursor), nil
}
func (s *DocumentService) GetVersion(
ctx context.Context,
documentVersionID gid.GID,
) (*coredata.DocumentVersion, error) {
documentVersion := &coredata.DocumentVersion{}
err := s.svc.pg.WithConn(
ctx,
func(conn pg.Conn) error {
return documentVersion.LoadByID(ctx, conn, s.svc.scope, documentVersionID)
},
)
if err != nil {
return nil, err
}
return documentVersion, nil
}
func (s *DocumentService) ListByOrganizationID(
ctx context.Context,
organizationID gid.GID,
cursor *page.Cursor[coredata.DocumentOrderField],
) (*page.Page[*coredata.Document, coredata.DocumentOrderField], error) {
var documents coredata.Documents
err := s.svc.pg.WithConn(
ctx,
func(conn pg.Conn) error {
return documents.LoadByOrganizationID(
ctx,
conn,
s.svc.scope,
organizationID,
cursor,
)
},
)
if err != nil {
return nil, err
}
return page.NewPage(documents, cursor), nil
}
func (s *DocumentService) ListForControlID(
ctx context.Context,
controlID gid.GID,
cursor *page.Cursor[coredata.DocumentOrderField],
) (*page.Page[*coredata.Document, coredata.DocumentOrderField], error) {
var documents coredata.Documents
err := s.svc.pg.WithConn(
ctx,
func(conn pg.Conn) error {
return documents.LoadByControlID(ctx, conn, s.svc.scope, controlID, cursor)
},
)
if err != nil {
return nil, err
}
return page.NewPage(documents, cursor), nil
}
func (s *DocumentService) ListForRiskID(
ctx context.Context,
riskID gid.GID,
cursor *page.Cursor[coredata.DocumentOrderField],
) (*page.Page[*coredata.Document, coredata.DocumentOrderField], error) {
var documents coredata.Documents
err := s.svc.pg.WithConn(
ctx,
func(conn pg.Conn) error {
return documents.LoadByRiskID(ctx, conn, s.svc.scope, riskID, cursor)
},
)
if err != nil {
return nil, err
}
return page.NewPage(documents, cursor), nil
}

View File

@@ -327,36 +327,36 @@ func (s FrameworkService) ExportAudit(
return fmt.Errorf("cannot load measures: %w", err)
}
policies := coredata.Policies{}
documents := coredata.Documents{}
cursor2 := page.NewCursor(
0,
nil,
page.Head,
page.OrderBy[coredata.PolicyOrderField]{
Field: coredata.PolicyOrderFieldCreatedAt,
page.OrderBy[coredata.DocumentOrderField]{
Field: coredata.DocumentOrderFieldCreatedAt,
Direction: page.OrderDirectionAsc,
},
)
if err := policies.LoadByControlID(ctx, conn, s.svc.scope, control.ID, cursor2); err != nil {
return fmt.Errorf("cannot load policies: %w", err)
if err := documents.LoadByControlID(ctx, conn, s.svc.scope, control.ID, cursor2); err != nil {
return fmt.Errorf("cannot load documents: %w", err)
}
for _, policy := range policies {
policyDir := filepath.Join(controlDir, policy.Title)
if err := os.MkdirAll(policyDir, 0755); err != nil {
return fmt.Errorf("cannot create policy directory: %w", err)
for _, document := range documents {
documentDir := filepath.Join(controlDir, document.Title)
if err := os.MkdirAll(documentDir, 0755); err != nil {
return fmt.Errorf("cannot create document directory: %w", err)
}
version := coredata.PolicyVersion{}
if err := version.LoadLatestVersion(ctx, conn, s.svc.scope, policy.ID); err != nil {
return fmt.Errorf("cannot load policy version: %w", err)
version := coredata.DocumentVersion{}
if err := version.LoadLatestVersion(ctx, conn, s.svc.scope, document.ID); err != nil {
return fmt.Errorf("cannot load document version: %w", err)
}
policyFile := filepath.Join(policyDir, "policy.md")
if err := os.WriteFile(policyFile, []byte(version.Content), 0644); err != nil {
return fmt.Errorf("cannot write policy file: %w", err)
documentFile := filepath.Join(documentDir, "document.md")
if err := os.WriteFile(documentFile, []byte(version.Content), 0644); err != nil {
return fmt.Errorf("cannot write document file: %w", err)
}
}

View File

@@ -1,634 +0,0 @@
package probo
import (
"context"
"fmt"
"net/url"
"time"
"github.com/getprobo/probo/pkg/coredata"
"github.com/getprobo/probo/pkg/gid"
"github.com/getprobo/probo/pkg/page"
"github.com/getprobo/probo/pkg/statelesstoken"
"github.com/jackc/pgx/v5"
"go.gearno.de/kit/pg"
)
type (
PolicyService struct {
svc *TenantService
}
CreatePolicyRequest struct {
OrganizationID gid.GID
Title string
Content string
OwnerID gid.GID
CreatedBy gid.GID
}
UpdatePolicyVersionRequest struct {
ID gid.GID
Content string
}
RequestSignatureRequest struct {
PolicyVersionID gid.GID
RequestedBy gid.GID
Signatory gid.GID
}
SigningRequestData struct {
OrganizationID gid.GID `json:"organization_id"`
PeopleID gid.GID `json:"people_id"`
}
)
const (
TokenTypeSigningRequest = "signing_request"
)
func (s *PolicyService) Get(
ctx context.Context,
policyID gid.GID,
) (*coredata.Policy, error) {
policy := &coredata.Policy{}
err := s.svc.pg.WithConn(
ctx,
func(conn pg.Conn) error {
return policy.LoadByID(ctx, conn, s.svc.scope, policyID)
},
)
if err != nil {
return nil, err
}
return policy, nil
}
func (s *PolicyService) PublishVersion(
ctx context.Context,
policyID gid.GID,
publishedBy gid.GID,
) (*coredata.Policy, *coredata.PolicyVersion, error) {
policy := &coredata.Policy{}
policyVersion := &coredata.PolicyVersion{}
now := time.Now()
err := s.svc.pg.WithTx(
ctx,
func(tx pg.Conn) error {
if err := policy.LoadByID(ctx, tx, s.svc.scope, policyID); err != nil {
return fmt.Errorf("cannot load policy %q: %w", policyID, err)
}
if err := policyVersion.LoadLatestVersion(ctx, tx, s.svc.scope, policyID); err != nil {
return fmt.Errorf("cannot load current draft: %w", err)
}
if policyVersion.Status != coredata.PolicyStatusDraft {
return fmt.Errorf("cannot publish version")
}
policy.CurrentPublishedVersion = &policyVersion.VersionNumber
policy.UpdatedAt = now
policyVersion.Status = coredata.PolicyStatusPublished
policyVersion.PublishedAt = &now
policyVersion.PublishedBy = &publishedBy
policyVersion.UpdatedAt = now
if err := policy.Update(ctx, tx, s.svc.scope); err != nil {
return fmt.Errorf("cannot update policy: %w", err)
}
if err := policyVersion.Update(ctx, tx, s.svc.scope); err != nil {
return fmt.Errorf("cannot update policy version: %w", err)
}
return nil
},
)
if err != nil {
return nil, nil, err
}
return policy, policyVersion, nil
}
func (s *PolicyService) Create(
ctx context.Context,
req CreatePolicyRequest,
) (*coredata.Policy, *coredata.PolicyVersion, error) {
now := time.Now()
policyID := gid.New(s.svc.scope.GetTenantID(), coredata.PolicyEntityType)
policyVersionID := gid.New(s.svc.scope.GetTenantID(), coredata.PolicyVersionEntityType)
organization := &coredata.Organization{}
people := &coredata.People{}
policy := &coredata.Policy{
ID: policyID,
Title: req.Title,
CreatedAt: now,
UpdatedAt: now,
}
policyVersion := &coredata.PolicyVersion{
ID: policyVersionID,
PolicyID: policyID,
VersionNumber: 1,
Content: req.Content,
Status: coredata.PolicyStatusDraft,
CreatedBy: req.CreatedBy,
CreatedAt: now,
UpdatedAt: now,
}
err := s.svc.pg.WithTx(
ctx,
func(conn pg.Conn) error {
if err := organization.LoadByID(ctx, conn, s.svc.scope, req.OrganizationID); err != nil {
return fmt.Errorf("cannot load organization: %w", err)
}
if err := people.LoadByID(ctx, conn, s.svc.scope, req.OwnerID); err != nil {
return fmt.Errorf("cannot load people: %w", err)
}
policy.OrganizationID = organization.ID
policy.OwnerID = people.ID
if err := policy.Insert(ctx, conn, s.svc.scope); err != nil {
return fmt.Errorf("cannot insert policy: %w", err)
}
if err := policyVersion.Insert(ctx, conn, s.svc.scope); err != nil {
return fmt.Errorf("cannot create policy version: %w", err)
}
return nil
},
)
if err != nil {
return nil, nil, err
}
return policy, policyVersion, nil
}
func (s *PolicyService) ListSigningRequests(
ctx context.Context,
organizationID gid.GID,
peopleID gid.GID,
) ([]map[string]any, error) {
q := `
SELECT
p.title,
pv.content,
pv.id AS policy_version_id
FROM
policies p
INNER JOIN policy_versions pv ON pv.policy_id = p.id
INNER JOIN policy_version_signatures pvs ON pvs.policy_version_id = pv.id
WHERE
p.tenant_id = $1
AND pvs.signed_by = $2
AND pvs.signed_at IS NULL
`
var results []map[string]any
err := s.svc.pg.WithConn(
ctx,
func(conn pg.Conn) error {
rows, err := conn.Query(ctx, q, s.svc.scope.GetTenantID(), peopleID)
if err != nil {
return fmt.Errorf("cannot query policies: %w", err)
}
results, err = pgx.CollectRows(rows, pgx.RowToMap)
if err != nil {
return err
}
return nil
},
)
if err != nil {
return nil, err
}
return results, nil
}
func (s *PolicyService) SendSigningNotifications(
ctx context.Context,
organizationID gid.GID,
) error {
err := s.svc.pg.WithTx(
ctx,
func(tx pg.Conn) error {
var peoples coredata.Peoples
if err := peoples.LoadAwaitingSigning(ctx, tx, s.svc.scope); err != nil {
return fmt.Errorf("cannot load people: %w", err)
}
for _, people := range peoples {
now := time.Now()
emailID := gid.New(s.svc.scope.GetTenantID(), coredata.EmailEntityType)
token, err := statelesstoken.NewToken(
s.svc.tokenSecret,
TokenTypeSigningRequest,
time.Hour*24*7,
SigningRequestData{
OrganizationID: organizationID,
PeopleID: people.ID,
},
)
if err != nil {
return fmt.Errorf("cannot create signing request token: %w", err)
}
signRequestURL := url.URL{
Scheme: "https",
Host: s.svc.hostname,
Path: "/policies/signing-requests",
RawQuery: url.Values{
"token": []string{token},
}.Encode(),
}
email := &coredata.Email{
ID: emailID,
RecipientEmail: people.PrimaryEmailAddress,
RecipientName: people.FullName,
Subject: "Probo - Policies Signing Request",
TextBody: fmt.Sprintf("Hi,\nYou have documents awaiting your signature. Please follow this link to sign them: %s", signRequestURL.String()),
CreatedAt: now,
UpdatedAt: now,
}
if err := email.Insert(ctx, tx); err != nil {
return fmt.Errorf("cannot insert email: %w", err)
}
}
return nil
},
)
if err != nil {
return fmt.Errorf("cannot send signing notifications: %w", err)
}
return nil
}
func (s *PolicyService) SignPolicyVersion(
ctx context.Context,
policyVersionID gid.GID,
signatory gid.GID,
) error {
policyVersion := &coredata.PolicyVersion{}
policyVersionSignature := &coredata.PolicyVersionSignature{}
now := time.Now()
err := s.svc.pg.WithTx(
ctx,
func(conn pg.Conn) error {
if err := policyVersion.LoadByID(ctx, conn, s.svc.scope, policyVersionID); err != nil {
return fmt.Errorf("cannot load policy version %q: %w", policyVersionID, err)
}
if policyVersion.Status != coredata.PolicyStatusPublished {
return fmt.Errorf("cannot sign unpublished version")
}
if err := policyVersionSignature.LoadByPolicyVersionIDAndSignatory(ctx, conn, s.svc.scope, policyVersionID, signatory); err != nil {
return fmt.Errorf("cannot load policy version signature: %w", err)
}
if policyVersionSignature.State == coredata.PolicyVersionSignatureStateSigned {
return fmt.Errorf("policy version already signed")
}
policyVersionSignature.State = coredata.PolicyVersionSignatureStateSigned
policyVersionSignature.SignedAt = &now
policyVersionSignature.UpdatedAt = now
if err := policyVersion.Update(ctx, conn, s.svc.scope); err != nil {
return fmt.Errorf("cannot update policy version: %w", err)
}
if err := policyVersionSignature.Update(ctx, conn, s.svc.scope); err != nil {
return fmt.Errorf("cannot update policy version signature: %w", err)
}
return nil
},
)
if err != nil {
return fmt.Errorf("cannot sign policy version: %w", err)
}
return nil
}
func (s *PolicyService) UpdateVersion(
ctx context.Context,
req UpdatePolicyVersionRequest,
) (*coredata.PolicyVersion, error) {
policyVersion := &coredata.PolicyVersion{}
err := s.svc.pg.WithTx(
ctx,
func(conn pg.Conn) error {
if err := policyVersion.LoadByID(ctx, conn, s.svc.scope, req.ID); err != nil {
return fmt.Errorf("cannot load policy version %q: %w", req.ID, err)
}
if policyVersion.Status != coredata.PolicyStatusDraft {
return fmt.Errorf("cannot update published version")
}
policyVersion.Content = req.Content
policyVersion.UpdatedAt = time.Now()
if err := policyVersion.Update(ctx, conn, s.svc.scope); err != nil {
return fmt.Errorf("cannot update policy version: %w", err)
}
return nil
},
)
if err != nil {
return nil, err
}
return policyVersion, nil
}
func (s *PolicyService) GetVersionSignature(
ctx context.Context,
signatureID gid.GID,
) (*coredata.PolicyVersionSignature, error) {
policyVersionSignature := &coredata.PolicyVersionSignature{}
err := s.svc.pg.WithConn(
ctx,
func(conn pg.Conn) error {
return policyVersionSignature.LoadByID(ctx, conn, s.svc.scope, signatureID)
},
)
if err != nil {
return nil, err
}
return policyVersionSignature, nil
}
func (s *PolicyService) RequestSignature(
ctx context.Context,
req RequestSignatureRequest,
) (*coredata.PolicyVersionSignature, error) {
policyVersionSignatureID := gid.New(s.svc.scope.GetTenantID(), coredata.PolicyVersionSignatureEntityType)
policyVersion, err := s.GetVersion(ctx, req.PolicyVersionID)
if err != nil {
return nil, fmt.Errorf("cannot get policy version: %w", err)
}
if policyVersion.Status != coredata.PolicyStatusPublished {
return nil, fmt.Errorf("cannot request signature for unpublished version")
}
now := time.Now()
policyVersionSignature := &coredata.PolicyVersionSignature{
ID: policyVersionSignatureID,
PolicyVersionID: req.PolicyVersionID,
State: coredata.PolicyVersionSignatureStateRequested,
RequestedBy: req.RequestedBy,
RequestedAt: now,
SignedBy: req.Signatory,
SignedAt: nil,
CreatedAt: now,
UpdatedAt: now,
}
err = s.svc.pg.WithTx(
ctx,
func(conn pg.Conn) error {
if err := policyVersionSignature.Insert(ctx, conn, s.svc.scope); err != nil {
return fmt.Errorf("cannot insert policy version signature: %w", err)
}
return nil
},
)
if err != nil {
return nil, err
}
return policyVersionSignature, nil
}
func (s *PolicyService) ListSignatures(
ctx context.Context,
policyVersionID gid.GID,
cursor *page.Cursor[coredata.PolicyVersionSignatureOrderField],
) (*page.Page[*coredata.PolicyVersionSignature, coredata.PolicyVersionSignatureOrderField], error) {
var policyVersionSignatures coredata.PolicyVersionSignatures
err := s.svc.pg.WithConn(
ctx,
func(conn pg.Conn) error {
return policyVersionSignatures.LoadByPolicyVersionID(ctx, conn, s.svc.scope, policyVersionID, cursor)
},
)
if err != nil {
return nil, err
}
return page.NewPage(policyVersionSignatures, cursor), nil
}
func (s *PolicyService) CreateDraft(
ctx context.Context,
policyID gid.GID,
createdBy gid.GID,
) (*coredata.PolicyVersion, error) {
draftVersionID := gid.New(s.svc.scope.GetTenantID(), coredata.PolicyVersionEntityType)
latestVersion := &coredata.PolicyVersion{}
draftVersion := &coredata.PolicyVersion{}
now := time.Now()
err := s.svc.pg.WithTx(
ctx,
func(conn pg.Conn) error {
if err := latestVersion.LoadLatestVersion(ctx, conn, s.svc.scope, policyID); err != nil {
return fmt.Errorf("cannot load latest version: %w", err)
}
if latestVersion.Status != coredata.PolicyStatusPublished {
return fmt.Errorf("cannot create draft from unpublished version")
}
draftVersion.ID = draftVersionID
draftVersion.PolicyID = policyID
draftVersion.VersionNumber = latestVersion.VersionNumber + 1
draftVersion.Content = latestVersion.Content
draftVersion.Status = coredata.PolicyStatusDraft
draftVersion.CreatedBy = createdBy
draftVersion.CreatedAt = now
draftVersion.UpdatedAt = now
if err := draftVersion.Insert(ctx, conn, s.svc.scope); err != nil {
return fmt.Errorf("cannot create draft: %w", err)
}
return nil
},
)
if err != nil {
return nil, err
}
return draftVersion, nil
}
func (s *PolicyService) Delete(
ctx context.Context,
policyID gid.GID,
) error {
policy := coredata.Policy{ID: policyID}
return s.svc.pg.WithConn(
ctx,
func(conn pg.Conn) error {
return policy.Delete(ctx, conn, s.svc.scope)
},
)
}
func (s *PolicyService) ListVersions(
ctx context.Context,
policyID gid.GID,
cursor *page.Cursor[coredata.PolicyVersionOrderField],
) (*page.Page[*coredata.PolicyVersion, coredata.PolicyVersionOrderField], error) {
var policyVersions coredata.PolicyVersions
err := s.svc.pg.WithConn(
ctx,
func(conn pg.Conn) error {
return policyVersions.LoadByPolicyID(ctx, conn, s.svc.scope, policyID, cursor)
},
)
if err != nil {
return nil, err
}
return page.NewPage(policyVersions, cursor), nil
}
func (s *PolicyService) GetVersion(
ctx context.Context,
policyVersionID gid.GID,
) (*coredata.PolicyVersion, error) {
policyVersion := &coredata.PolicyVersion{}
err := s.svc.pg.WithConn(
ctx,
func(conn pg.Conn) error {
return policyVersion.LoadByID(ctx, conn, s.svc.scope, policyVersionID)
},
)
if err != nil {
return nil, err
}
return policyVersion, nil
}
func (s *PolicyService) ListByOrganizationID(
ctx context.Context,
organizationID gid.GID,
cursor *page.Cursor[coredata.PolicyOrderField],
) (*page.Page[*coredata.Policy, coredata.PolicyOrderField], error) {
var policies coredata.Policies
err := s.svc.pg.WithConn(
ctx,
func(conn pg.Conn) error {
return policies.LoadByOrganizationID(
ctx,
conn,
s.svc.scope,
organizationID,
cursor,
)
},
)
if err != nil {
return nil, err
}
return page.NewPage(policies, cursor), nil
}
func (s *PolicyService) ListForControlID(
ctx context.Context,
controlID gid.GID,
cursor *page.Cursor[coredata.PolicyOrderField],
) (*page.Page[*coredata.Policy, coredata.PolicyOrderField], error) {
var policies coredata.Policies
err := s.svc.pg.WithConn(
ctx,
func(conn pg.Conn) error {
return policies.LoadByControlID(ctx, conn, s.svc.scope, controlID, cursor)
},
)
if err != nil {
return nil, err
}
return page.NewPage(policies, cursor), nil
}
func (s *PolicyService) ListForRiskID(
ctx context.Context,
riskID gid.GID,
cursor *page.Cursor[coredata.PolicyOrderField],
) (*page.Page[*coredata.Policy, coredata.PolicyOrderField], error) {
var policies coredata.Policies
err := s.svc.pg.WithConn(
ctx,
func(conn pg.Conn) error {
return policies.LoadByRiskID(ctx, conn, s.svc.scope, riskID, cursor)
},
)
if err != nil {
return nil, err
}
return page.NewPage(policies, cursor), nil
}

View File

@@ -80,13 +80,13 @@ func (s RiskService) ListForMeasureID(
return page.NewPage(risks, cursor), nil
}
func (s RiskService) CreatePolicyMapping(
func (s RiskService) CreateDocumentMapping(
ctx context.Context,
riskID gid.GID,
policyID gid.GID,
) (*coredata.Risk, *coredata.Policy, error) {
documentID gid.GID,
) (*coredata.Risk, *coredata.Document, error) {
risk := &coredata.Risk{}
policy := &coredata.Policy{}
document := &coredata.Document{}
err := s.svc.pg.WithConn(
ctx,
@@ -95,36 +95,36 @@ func (s RiskService) CreatePolicyMapping(
return fmt.Errorf("cannot load risk: %w", err)
}
if err := policy.LoadByID(ctx, conn, s.svc.scope, policyID); err != nil {
return fmt.Errorf("cannot load policy: %w", err)
if err := document.LoadByID(ctx, conn, s.svc.scope, documentID); err != nil {
return fmt.Errorf("cannot load document: %w", err)
}
riskPolicy := &coredata.RiskPolicy{
RiskID: risk.ID,
PolicyID: policy.ID,
TenantID: s.svc.scope.GetTenantID(),
CreatedAt: time.Now(),
riskDocument := &coredata.RiskDocument{
RiskID: risk.ID,
DocumentID: document.ID,
TenantID: s.svc.scope.GetTenantID(),
CreatedAt: time.Now(),
}
return riskPolicy.Insert(ctx, conn, s.svc.scope)
return riskDocument.Insert(ctx, conn, s.svc.scope)
},
)
if err != nil {
return nil, nil, fmt.Errorf("cannot create risk policy mapping: %w", err)
return nil, nil, fmt.Errorf("cannot create risk document mapping: %w", err)
}
return risk, policy, nil
return risk, document, nil
}
func (s RiskService) DeletePolicyMapping(
func (s RiskService) DeleteDocumentMapping(
ctx context.Context,
riskID gid.GID,
policyID gid.GID,
) (*coredata.Risk, *coredata.Policy, error) {
riskPolicy := &coredata.RiskPolicy{}
documentID gid.GID,
) (*coredata.Risk, *coredata.Document, error) {
riskDocument := &coredata.RiskDocument{}
risk := &coredata.Risk{}
policy := &coredata.Policy{}
document := &coredata.Document{}
err := s.svc.pg.WithConn(
ctx,
@@ -133,19 +133,19 @@ func (s RiskService) DeletePolicyMapping(
return fmt.Errorf("cannot load risk: %w", err)
}
if err := policy.LoadByID(ctx, conn, s.svc.scope, policyID); err != nil {
return fmt.Errorf("cannot load policy: %w", err)
if err := document.LoadByID(ctx, conn, s.svc.scope, documentID); err != nil {
return fmt.Errorf("cannot load document: %w", err)
}
return riskPolicy.Delete(ctx, conn, s.svc.scope, risk.ID, policy.ID)
return riskDocument.Delete(ctx, conn, s.svc.scope, risk.ID, document.ID)
},
)
if err != nil {
return nil, nil, fmt.Errorf("cannot delete risk policy mapping: %w", err)
return nil, nil, fmt.Errorf("cannot delete risk document mapping: %w", err)
}
return risk, policy, nil
return risk, document, nil
}
func (s RiskService) CreateMeasureMapping(

View File

@@ -54,7 +54,7 @@ type (
Organizations *OrganizationService
Vendors *VendorService
Peoples *PeopleService
Policies *PolicyService
Documents *DocumentService
Controls *ControlService
Risks *RiskService
VendorComplianceReports *VendorComplianceReportService
@@ -118,7 +118,7 @@ func (s *Service) WithTenant(tenantID gid.TenantID) *TenantService {
}
tenantService.Peoples = &PeopleService{svc: tenantService}
tenantService.Vendors = &VendorService{svc: tenantService}
tenantService.Policies = &PolicyService{svc: tenantService}
tenantService.Documents = &DocumentService{svc: tenantService}
tenantService.Organizations = &OrganizationService{
svc: tenantService,
fileValidator: filevalidation.NewValidator(

View File

@@ -89,7 +89,7 @@ func NewMux(
r := chi.NewMux()
r.Get(
"/policies/signing-requests",
"/documents/signing-requests",
func(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("Authorization")
if token == "" {
@@ -106,7 +106,7 @@ func NewMux(
svc := proboSvc.WithTenant(data.Data.OrganizationID.TenantID())
requests, err := svc.Policies.ListSigningRequests(r.Context(), data.Data.OrganizationID, data.Data.PeopleID)
requests, err := svc.Documents.ListSigningRequests(r.Context(), data.Data.OrganizationID, data.Data.PeopleID)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
@@ -118,7 +118,7 @@ func NewMux(
)
r.Post(
"/policies/signing-requests/{policy_version_id}/sign",
"/documents/signing-requests/{document_version_id}/sign",
func(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("Authorization")
if token == "" {
@@ -133,15 +133,15 @@ func NewMux(
return
}
policyVersionID, err := gid.ParseGID(chi.URLParam(r, "policy_version_id"))
documentVersionID, err := gid.ParseGID(chi.URLParam(r, "document_version_id"))
if err != nil {
http.Error(w, "invalid policy version id", http.StatusBadRequest)
http.Error(w, "invalid document version id", http.StatusBadRequest)
return
}
svc := proboSvc.WithTenant(data.Data.OrganizationID.TenantID())
if err := svc.Policies.SignPolicyVersion(r.Context(), policyVersionID, data.Data.PeopleID); err != nil {
if err := svc.Documents.SignDocumentVersion(r.Context(), documentVersionID, data.Data.PeopleID); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

View File

@@ -91,13 +91,13 @@ enum PeopleKind
)
}
enum PolicyStatus
@goModel(model: "github.com/getprobo/probo/pkg/coredata.PolicyStatus") {
enum DocumentStatus
@goModel(model: "github.com/getprobo/probo/pkg/coredata.DocumentStatus") {
DRAFT
@goEnum(value: "github.com/getprobo/probo/pkg/coredata.PolicyStatusDraft")
@goEnum(value: "github.com/getprobo/probo/pkg/coredata.DocumentStatusDraft")
PUBLISHED
@goEnum(
value: "github.com/getprobo/probo/pkg/coredata.PolicyStatusPublished"
value: "github.com/getprobo/probo/pkg/coredata.DocumentStatusPublished"
)
}
@@ -184,15 +184,15 @@ enum TaskOrderField
CREATED_AT
}
enum PolicyOrderField
@goModel(model: "github.com/getprobo/probo/pkg/coredata.PolicyOrderField") {
enum DocumentOrderField
@goModel(model: "github.com/getprobo/probo/pkg/coredata.DocumentOrderField") {
TITLE
@goEnum(
value: "github.com/getprobo/probo/pkg/coredata.PolicyOrderFieldTitle"
value: "github.com/getprobo/probo/pkg/coredata.DocumentOrderFieldTitle"
)
CREATED_AT
@goEnum(
value: "github.com/getprobo/probo/pkg/coredata.PolicyOrderFieldCreatedAt"
value: "github.com/getprobo/probo/pkg/coredata.DocumentOrderFieldCreatedAt"
)
}
@@ -298,17 +298,17 @@ enum BusinessImpact
)
}
enum PolicyVersionOrderField
enum DocumentVersionOrderField
@goModel(
model: "github.com/getprobo/probo/pkg/coredata.PolicyVersionOrderField"
model: "github.com/getprobo/probo/pkg/coredata.DocumentVersionOrderField"
) {
VERSION
@goEnum(
value: "github.com/getprobo/probo/pkg/coredata.PolicyVersionOrderFieldVersion"
value: "github.com/getprobo/probo/pkg/coredata.DocumentVersionOrderFieldVersion"
)
CREATED_AT
@goEnum(
value: "github.com/getprobo/probo/pkg/coredata.PolicyVersionOrderFieldCreatedAt"
value: "github.com/getprobo/probo/pkg/coredata.DocumentVersionOrderFieldCreatedAt"
)
}
@@ -394,12 +394,12 @@ input TaskOrder
field: TaskOrderField!
}
input PolicyOrder
input DocumentOrder
@goModel(
model: "github.com/getprobo/probo/pkg/server/api/console/v1/types.PolicyOrderBy"
model: "github.com/getprobo/probo/pkg/server/api/console/v1/types.DocumentOrderBy"
) {
direction: OrderDirection!
field: PolicyOrderField!
field: DocumentOrderField!
}
input RiskOrder
@@ -436,16 +436,16 @@ input ConnectorOrder {
direction: OrderDirection!
}
input PolicyVersionOrder
input DocumentVersionOrder
@goModel(
model: "github.com/getprobo/probo/pkg/server/api/console/v1/types.PolicyVersionOrderBy"
model: "github.com/getprobo/probo/pkg/server/api/console/v1/types.DocumentVersionOrderBy"
) {
direction: OrderDirection!
field: PolicyVersionOrderField!
field: DocumentVersionOrderField!
}
input PolicyVersionFilter {
status: PolicyStatus
input DocumentVersionFilter {
status: DocumentStatus
}
# Core Types
@@ -494,13 +494,13 @@ type Organization implements Node {
orderBy: PeopleOrder
): PeopleConnection! @goField(forceResolver: true)
policies(
documents(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: PolicyOrder
): PolicyConnection! @goField(forceResolver: true)
orderBy: DocumentOrder
): DocumentConnection! @goField(forceResolver: true)
measures(
first: Int
@@ -652,13 +652,13 @@ type Control implements Node {
orderBy: MeasureOrder
): MeasureConnection! @goField(forceResolver: true)
policies(
documents(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: PolicyOrder
): PolicyConnection! @goField(forceResolver: true)
orderBy: DocumentOrder
): DocumentConnection! @goField(forceResolver: true)
createdAt: Datetime!
updatedAt: Datetime!
@@ -748,7 +748,7 @@ type Evidence implements Node {
updatedAt: Datetime!
}
type Policy implements Node {
type Document implements Node {
id: ID!
title: String!
description: String!
@@ -761,9 +761,9 @@ type Policy implements Node {
after: CursorKey
last: Int
before: CursorKey
orderBy: PolicyVersionOrder
filter: PolicyVersionFilter
): PolicyVersionConnection! @goField(forceResolver: true)
orderBy: DocumentVersionOrder
filter: DocumentVersionFilter
): DocumentVersionConnection! @goField(forceResolver: true)
controls(
first: Int
@@ -802,13 +802,13 @@ type Risk implements Node {
orderBy: MeasureOrder
): MeasureConnection! @goField(forceResolver: true)
policies(
documents(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: PolicyOrder
): PolicyConnection! @goField(forceResolver: true)
orderBy: DocumentOrder
): DocumentConnection! @goField(forceResolver: true)
controls(
first: Int
@@ -931,14 +931,14 @@ type EvidenceEdge {
node: Evidence!
}
type PolicyConnection {
edges: [PolicyEdge!]!
type DocumentConnection {
edges: [DocumentEdge!]!
pageInfo: PageInfo!
}
type PolicyEdge {
type DocumentEdge {
cursor: CursorKey!
node: Policy!
node: Document!
}
type RiskConnection {
@@ -981,14 +981,14 @@ type VendorRiskAssessmentEdge {
node: VendorRiskAssessment!
}
type PolicyVersionConnection {
edges: [PolicyVersionEdge!]!
type DocumentVersionConnection {
edges: [DocumentVersionEdge!]!
pageInfo: PageInfo!
}
type PolicyVersionEdge {
type DocumentVersionEdge {
cursor: CursorKey!
node: PolicyVersion!
node: DocumentVersion!
}
# Root Types
@@ -1040,15 +1040,15 @@ type Mutation {
createControlMeasureMapping(
input: CreateControlMeasureMappingInput!
): CreateControlMeasureMappingPayload!
createControlPolicyMapping(
input: CreateControlPolicyMappingInput!
): CreateControlPolicyMappingPayload!
createControlDocumentMapping(
input: CreateControlDocumentMappingInput!
): CreateControlDocumentMappingPayload!
deleteControlMeasureMapping(
input: DeleteControlMeasureMappingInput!
): DeleteControlMeasureMappingPayload!
deleteControlPolicyMapping(
input: DeleteControlPolicyMappingInput!
): DeleteControlPolicyMappingPayload!
deleteControlDocumentMapping(
input: DeleteControlDocumentMappingInput!
): DeleteControlDocumentMappingPayload!
# Task mutations
createTask(input: CreateTaskInput!): CreateTaskPayload!
@@ -1068,12 +1068,12 @@ type Mutation {
input: DeleteRiskMeasureMappingInput!
): DeleteRiskMeasureMappingPayload!
createRiskPolicyMapping(
input: CreateRiskPolicyMappingInput!
): CreateRiskPolicyMappingPayload!
deleteRiskPolicyMapping(
input: DeleteRiskPolicyMappingInput!
): DeleteRiskPolicyMappingPayload!
createRiskDocumentMapping(
input: CreateRiskDocumentMappingInput!
): CreateRiskDocumentMappingPayload!
deleteRiskDocumentMapping(
input: DeleteRiskDocumentMappingInput!
): DeleteRiskDocumentMappingPayload!
# Evidence mutations
requestEvidence(input: RequestEvidenceInput!): RequestEvidencePayload!
@@ -1094,18 +1094,18 @@ type Mutation {
input: DeleteVendorComplianceReportInput!
): DeleteVendorComplianceReportPayload!
# Policy mutations
createPolicy(input: CreatePolicyInput!): CreatePolicyPayload!
deletePolicy(input: DeletePolicyInput!): DeletePolicyPayload!
publishPolicyVersion(
input: PublishPolicyVersionInput!
): PublishPolicyVersionPayload!
createDraftPolicyVersion(
input: CreateDraftPolicyVersionInput!
): CreateDraftPolicyVersionPayload!
updatePolicyVersion(
input: UpdatePolicyVersionInput!
): UpdatePolicyVersionPayload!
# Document mutations
createDocument(input: CreateDocumentInput!): CreateDocumentPayload!
deleteDocument(input: DeleteDocumentInput!): DeleteDocumentPayload!
publishDocumentVersion(
input: PublishDocumentVersionInput!
): PublishDocumentVersionPayload!
createDraftDocumentVersion(
input: CreateDraftDocumentVersionInput!
): CreateDraftDocumentVersionPayload!
updateDocumentVersion(
input: UpdateDocumentVersionInput!
): UpdateDocumentVersionPayload!
requestSignature(input: RequestSignatureInput!): RequestSignaturePayload!
sendSigningNotifications(
input: SendSigningNotificationsInput!
@@ -1285,9 +1285,9 @@ input CreateControlMeasureMappingInput {
measureId: ID!
}
input CreateControlPolicyMappingInput {
input CreateControlDocumentMappingInput {
controlId: ID!
policyId: ID!
documentId: ID!
}
input DeleteControlMeasureMappingInput {
@@ -1295,9 +1295,9 @@ input DeleteControlMeasureMappingInput {
measureId: ID!
}
input DeleteControlPolicyMappingInput {
input DeleteControlDocumentMappingInput {
controlId: ID!
policyId: ID!
documentId: ID!
}
input CreateRiskInput {
@@ -1342,14 +1342,14 @@ input DeleteRiskMeasureMappingInput {
measureId: ID!
}
input CreateRiskPolicyMappingInput {
input CreateRiskDocumentMappingInput {
riskId: ID!
policyId: ID!
documentId: ID!
}
input DeleteRiskPolicyMappingInput {
input DeleteRiskDocumentMappingInput {
riskId: ID!
policyId: ID!
documentId: ID!
}
input RequestEvidenceInput {
@@ -1391,14 +1391,14 @@ input DeleteVendorComplianceReportInput {
reportId: ID!
}
input CreatePolicyInput {
input CreateDocumentInput {
organizationId: ID!
title: String!
content: String!
ownerId: ID!
}
input UpdatePolicyInput {
input UpdateDocumentInput {
id: ID!
title: String
content: String
@@ -1406,8 +1406,8 @@ input UpdatePolicyInput {
createdBy: ID
}
input DeletePolicyInput {
policyId: ID!
input DeleteDocumentInput {
documentId: ID!
}
input ConfirmEmailInput {
@@ -1515,9 +1515,9 @@ type CreateControlMeasureMappingPayload {
measureEdge: MeasureEdge!
}
type CreateControlPolicyMappingPayload {
type CreateControlDocumentMappingPayload {
controlEdge: ControlEdge!
policyEdge: PolicyEdge!
documentEdge: DocumentEdge!
}
type DeleteControlMeasureMappingPayload {
@@ -1525,9 +1525,9 @@ type DeleteControlMeasureMappingPayload {
deletedMeasureId: ID!
}
type DeleteControlPolicyMappingPayload {
type DeleteControlDocumentMappingPayload {
deletedControlId: ID!
deletedPolicyId: ID!
deletedDocumentId: ID!
}
type CreateRiskPayload {
@@ -1552,14 +1552,14 @@ type DeleteRiskMeasureMappingPayload {
deletedRiskId: ID!
}
type CreateRiskPolicyMappingPayload {
type CreateRiskDocumentMappingPayload {
riskEdge: RiskEdge!
policyEdge: PolicyEdge!
documentEdge: DocumentEdge!
}
type DeleteRiskPolicyMappingPayload {
type DeleteRiskDocumentMappingPayload {
deletedRiskId: ID!
deletedPolicyId: ID!
deletedDocumentId: ID!
}
type RequestEvidencePayload {
@@ -1586,17 +1586,17 @@ type DeleteVendorComplianceReportPayload {
deletedVendorComplianceReportId: ID!
}
type CreatePolicyPayload {
policyEdge: PolicyEdge!
policyVersionEdge: PolicyVersionEdge!
type CreateDocumentPayload {
documentEdge: DocumentEdge!
documentVersionEdge: DocumentVersionEdge!
}
type UpdatePolicyPayload {
policy: Policy!
type UpdateDocumentPayload {
document: Document!
}
type DeletePolicyPayload {
deletedPolicyId: ID!
type DeleteDocumentPayload {
deletedDocumentId: ID!
}
type ConfirmEmailPayload {
@@ -1668,10 +1668,10 @@ type DeleteMeasurePayload {
deletedMeasureId: ID!
}
type PolicyVersion implements Node {
type DocumentVersion implements Node {
id: ID!
policy: Policy! @goField(forceResolver: true)
status: PolicyStatus!
document: Document! @goField(forceResolver: true)
status: DocumentStatus!
version: Int!
content: String!
changelog: String!
@@ -1681,8 +1681,8 @@ type PolicyVersion implements Node {
after: CursorKey
last: Int
before: CursorKey
orderBy: PolicyVersionSignatureOrder
): PolicyVersionSignatureConnection! @goField(forceResolver: true)
orderBy: DocumentVersionSignatureOrder
): DocumentVersionSignatureConnection! @goField(forceResolver: true)
publishedBy: People @goField(forceResolver: true)
publishedAt: Datetime
@@ -1690,53 +1690,53 @@ type PolicyVersion implements Node {
updatedAt: Datetime!
}
type PolicyVersionSignatureConnection {
edges: [PolicyVersionSignatureEdge!]!
type DocumentVersionSignatureConnection {
edges: [DocumentVersionSignatureEdge!]!
pageInfo: PageInfo!
}
type PolicyVersionSignatureEdge {
type DocumentVersionSignatureEdge {
cursor: CursorKey!
node: PolicyVersionSignature!
node: DocumentVersionSignature!
}
input PolicyVersionSignatureOrder {
field: PolicyVersionSignatureOrderField!
input DocumentVersionSignatureOrder {
field: DocumentVersionSignatureOrderField!
direction: OrderDirection!
}
enum PolicyVersionSignatureState
enum DocumentVersionSignatureState
@goModel(
model: "github.com/getprobo/probo/pkg/coredata.PolicyVersionSignatureState"
model: "github.com/getprobo/probo/pkg/coredata.DocumentVersionSignatureState"
) {
REQUESTED
@goEnum(
value: "github.com/getprobo/probo/pkg/coredata.PolicyVersionSignatureStateRequested"
value: "github.com/getprobo/probo/pkg/coredata.DocumentVersionSignatureStateRequested"
)
SIGNED
@goEnum(
value: "github.com/getprobo/probo/pkg/coredata.PolicyVersionSignatureStateSigned"
value: "github.com/getprobo/probo/pkg/coredata.DocumentVersionSignatureStateSigned"
)
}
enum PolicyVersionSignatureOrderField
enum DocumentVersionSignatureOrderField
@goModel(
model: "github.com/getprobo/probo/pkg/coredata.PolicyVersionSignatureOrderField"
model: "github.com/getprobo/probo/pkg/coredata.DocumentVersionSignatureOrderField"
) {
CREATED_AT
@goEnum(
value: "github.com/getprobo/probo/pkg/coredata.PolicyVersionSignatureOrderFieldCreatedAt"
value: "github.com/getprobo/probo/pkg/coredata.DocumentVersionSignatureOrderFieldCreatedAt"
)
SIGNED_AT
@goEnum(
value: "github.com/getprobo/probo/pkg/coredata.PolicyVersionSignatureOrderFieldSignedAt"
value: "github.com/getprobo/probo/pkg/coredata.DocumentVersionSignatureOrderFieldSignedAt"
)
}
type PolicyVersionSignature implements Node {
type DocumentVersionSignature implements Node {
id: ID!
policyVersion: PolicyVersion! @goField(forceResolver: true)
state: PolicyVersionSignatureState!
documentVersion: DocumentVersion! @goField(forceResolver: true)
state: DocumentVersionSignatureState!
signedBy: People! @goField(forceResolver: true)
signedAt: Datetime
requestedAt: Datetime!
@@ -1746,38 +1746,38 @@ type PolicyVersionSignature implements Node {
}
input RequestSignatureInput {
policyVersionId: ID!
documentVersionId: ID!
signatoryId: ID!
}
type RequestSignaturePayload {
policyVersionSignatureEdge: PolicyVersionSignatureEdge!
documentVersionSignatureEdge: DocumentVersionSignatureEdge!
}
input PublishPolicyVersionInput {
policyId: ID!
input PublishDocumentVersionInput {
documentId: ID!
}
type PublishPolicyVersionPayload {
policyVersion: PolicyVersion!
policy: Policy!
type PublishDocumentVersionPayload {
documentVersion: DocumentVersion!
document: Document!
}
type CreateDraftPolicyVersionPayload {
policyVersionEdge: PolicyVersionEdge!
type CreateDraftDocumentVersionPayload {
documentVersionEdge: DocumentVersionEdge!
}
input CreateDraftPolicyVersionInput {
policyID: ID!
input CreateDraftDocumentVersionInput {
documentID: ID!
}
input UpdatePolicyVersionInput {
policyVersionId: ID!
input UpdateDocumentVersionInput {
documentVersionId: ID!
content: String!
}
type UpdatePolicyVersionPayload {
policyVersion: PolicyVersion!
type UpdateDocumentVersionPayload {
documentVersion: DocumentVersion!
}
input SendSigningNotificationsInput {

File diff suppressed because it is too large Load Diff

View File

@@ -20,34 +20,34 @@ import (
)
type (
PolicyOrderBy OrderBy[coredata.PolicyOrderField]
DocumentOrderBy OrderBy[coredata.DocumentOrderField]
)
func NewPolicyConnection(page *page.Page[*coredata.Policy, coredata.PolicyOrderField]) *PolicyConnection {
edges := make([]*PolicyEdge, len(page.Data))
for i, policy := range page.Data {
edges[i] = NewPolicyEdge(policy, page.Cursor.OrderBy.Field)
func NewDocumentConnection(page *page.Page[*coredata.Document, coredata.DocumentOrderField]) *DocumentConnection {
edges := make([]*DocumentEdge, len(page.Data))
for i, document := range page.Data {
edges[i] = NewDocumentEdge(document, page.Cursor.OrderBy.Field)
}
return &PolicyConnection{
return &DocumentConnection{
Edges: edges,
PageInfo: NewPageInfo(page),
}
}
func NewPolicyEdge(policy *coredata.Policy, orderBy coredata.PolicyOrderField) *PolicyEdge {
return &PolicyEdge{
Cursor: policy.CursorKey(orderBy),
Node: NewPolicy(policy),
func NewDocumentEdge(document *coredata.Document, orderBy coredata.DocumentOrderField) *DocumentEdge {
return &DocumentEdge{
Cursor: document.CursorKey(orderBy),
Node: NewDocument(document),
}
}
func NewPolicy(policy *coredata.Policy) *Policy {
return &Policy{
ID: policy.ID,
Title: policy.Title,
CurrentPublishedVersion: policy.CurrentPublishedVersion,
CreatedAt: policy.CreatedAt,
UpdatedAt: policy.UpdatedAt,
func NewDocument(document *coredata.Document) *Document {
return &Document{
ID: document.ID,
Title: document.Title,
CurrentPublishedVersion: document.CurrentPublishedVersion,
CreatedAt: document.CreatedAt,
UpdatedAt: document.UpdatedAt,
}
}

View File

@@ -0,0 +1,56 @@
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
package types
import (
"github.com/getprobo/probo/pkg/coredata"
"github.com/getprobo/probo/pkg/page"
)
type (
DocumentVersionOrderBy OrderBy[coredata.DocumentVersionOrderField]
)
func NewDocumentVersionConnection(page *page.Page[*coredata.DocumentVersion, coredata.DocumentVersionOrderField]) *DocumentVersionConnection {
edges := make([]*DocumentVersionEdge, len(page.Data))
for i, documentVersion := range page.Data {
edges[i] = NewDocumentVersionEdge(documentVersion, page.Cursor.OrderBy.Field)
}
return &DocumentVersionConnection{
Edges: edges,
PageInfo: NewPageInfo(page),
}
}
func NewDocumentVersionEdge(documentVersion *coredata.DocumentVersion, orderBy coredata.DocumentVersionOrderField) *DocumentVersionEdge {
return &DocumentVersionEdge{
Cursor: documentVersion.CursorKey(orderBy),
Node: NewDocumentVersion(documentVersion),
}
}
func NewDocumentVersion(documentVersion *coredata.DocumentVersion) *DocumentVersion {
return &DocumentVersion{
ID: documentVersion.ID,
Version: documentVersion.VersionNumber,
Content: documentVersion.Content,
Status: documentVersion.Status,
PublishedAt: documentVersion.PublishedAt,
Changelog: documentVersion.Changelog,
CreatedAt: documentVersion.CreatedAt,
UpdatedAt: documentVersion.UpdatedAt,
}
}

View File

@@ -0,0 +1,54 @@
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
package types
import (
"github.com/getprobo/probo/pkg/coredata"
"github.com/getprobo/probo/pkg/page"
)
type (
DocumentVersionSignatureOrderBy OrderBy[coredata.DocumentVersionSignatureOrderField]
)
func NewDocumentVersionSignatureConnection(page *page.Page[*coredata.DocumentVersionSignature, coredata.DocumentVersionSignatureOrderField]) *DocumentVersionSignatureConnection {
edges := make([]*DocumentVersionSignatureEdge, len(page.Data))
for i, documentVersionSignature := range page.Data {
edges[i] = NewDocumentVersionSignatureEdge(documentVersionSignature, page.Cursor.OrderBy.Field)
}
return &DocumentVersionSignatureConnection{
Edges: edges,
PageInfo: NewPageInfo(page),
}
}
func NewDocumentVersionSignatureEdge(documentVersionSignature *coredata.DocumentVersionSignature, orderBy coredata.DocumentVersionSignatureOrderField) *DocumentVersionSignatureEdge {
return &DocumentVersionSignatureEdge{
Cursor: documentVersionSignature.CursorKey(orderBy),
Node: NewDocumentVersionSignature(documentVersionSignature),
}
}
func NewDocumentVersionSignature(documentVersionSignature *coredata.DocumentVersionSignature) *DocumentVersionSignature {
return &DocumentVersionSignature{
ID: documentVersionSignature.ID,
State: documentVersionSignature.State,
SignedAt: documentVersionSignature.SignedAt,
RequestedAt: documentVersionSignature.RequestedAt,
CreatedAt: documentVersionSignature.CreatedAt,
UpdatedAt: documentVersionSignature.UpdatedAt,
}
}

View File

@@ -1,56 +0,0 @@
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
package types
import (
"github.com/getprobo/probo/pkg/coredata"
"github.com/getprobo/probo/pkg/page"
)
type (
PolicyVersionOrderBy OrderBy[coredata.PolicyVersionOrderField]
)
func NewPolicyVersionConnection(page *page.Page[*coredata.PolicyVersion, coredata.PolicyVersionOrderField]) *PolicyVersionConnection {
edges := make([]*PolicyVersionEdge, len(page.Data))
for i, policyVersion := range page.Data {
edges[i] = NewPolicyVersionEdge(policyVersion, page.Cursor.OrderBy.Field)
}
return &PolicyVersionConnection{
Edges: edges,
PageInfo: NewPageInfo(page),
}
}
func NewPolicyVersionEdge(policyVersion *coredata.PolicyVersion, orderBy coredata.PolicyVersionOrderField) *PolicyVersionEdge {
return &PolicyVersionEdge{
Cursor: policyVersion.CursorKey(orderBy),
Node: NewPolicyVersion(policyVersion),
}
}
func NewPolicyVersion(policyVersion *coredata.PolicyVersion) *PolicyVersion {
return &PolicyVersion{
ID: policyVersion.ID,
Version: policyVersion.VersionNumber,
Content: policyVersion.Content,
Status: policyVersion.Status,
PublishedAt: policyVersion.PublishedAt,
Changelog: policyVersion.Changelog,
CreatedAt: policyVersion.CreatedAt,
UpdatedAt: policyVersion.UpdatedAt,
}
}

View File

@@ -1,54 +0,0 @@
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
package types
import (
"github.com/getprobo/probo/pkg/coredata"
"github.com/getprobo/probo/pkg/page"
)
type (
PolicyVersionSignatureOrderBy OrderBy[coredata.PolicyVersionSignatureOrderField]
)
func NewPolicyVersionSignatureConnection(page *page.Page[*coredata.PolicyVersionSignature, coredata.PolicyVersionSignatureOrderField]) *PolicyVersionSignatureConnection {
edges := make([]*PolicyVersionSignatureEdge, len(page.Data))
for i, policyVersionSignature := range page.Data {
edges[i] = NewPolicyVersionSignatureEdge(policyVersionSignature, page.Cursor.OrderBy.Field)
}
return &PolicyVersionSignatureConnection{
Edges: edges,
PageInfo: NewPageInfo(page),
}
}
func NewPolicyVersionSignatureEdge(policyVersionSignature *coredata.PolicyVersionSignature, orderBy coredata.PolicyVersionSignatureOrderField) *PolicyVersionSignatureEdge {
return &PolicyVersionSignatureEdge{
Cursor: policyVersionSignature.CursorKey(orderBy),
Node: NewPolicyVersionSignature(policyVersionSignature),
}
}
func NewPolicyVersionSignature(policyVersionSignature *coredata.PolicyVersionSignature) *PolicyVersionSignature {
return &PolicyVersionSignature{
ID: policyVersionSignature.ID,
State: policyVersionSignature.State,
SignedAt: policyVersionSignature.SignedAt,
RequestedAt: policyVersionSignature.RequestedAt,
CreatedAt: policyVersionSignature.CreatedAt,
UpdatedAt: policyVersionSignature.UpdatedAt,
}
}

View File

@@ -79,7 +79,7 @@ type Control struct {
Description string `json:"description"`
Framework *Framework `json:"framework"`
Measures *MeasureConnection `json:"measures"`
Policies *PolicyConnection `json:"policies"`
Documents *DocumentConnection `json:"documents"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
@@ -107,22 +107,22 @@ type CreateControlMeasureMappingPayload struct {
MeasureEdge *MeasureEdge `json:"measureEdge"`
}
type CreateControlPolicyMappingInput struct {
type CreateControlDocumentMappingInput struct {
ControlID gid.GID `json:"controlId"`
PolicyID gid.GID `json:"policyId"`
DocumentID gid.GID `json:"documentId"`
}
type CreateControlPolicyMappingPayload struct {
type CreateControlDocumentMappingPayload struct {
ControlEdge *ControlEdge `json:"controlEdge"`
PolicyEdge *PolicyEdge `json:"policyEdge"`
DocumentEdge *DocumentEdge `json:"documentEdge"`
}
type CreateDraftPolicyVersionInput struct {
PolicyID gid.GID `json:"policyID"`
type CreateDraftDocumentVersionInput struct {
DocumentID gid.GID `json:"documentID"`
}
type CreateDraftPolicyVersionPayload struct {
PolicyVersionEdge *PolicyVersionEdge `json:"policyVersionEdge"`
type CreateDraftDocumentVersionPayload struct {
DocumentVersionEdge *DocumentVersionEdge `json:"documentVersionEdge"`
}
type CreateEvidenceInput struct {
@@ -182,16 +182,16 @@ type CreatePeoplePayload struct {
PeopleEdge *PeopleEdge `json:"peopleEdge"`
}
type CreatePolicyInput struct {
type CreateDocumentInput struct {
OrganizationID gid.GID `json:"organizationId"`
Title string `json:"title"`
Content string `json:"content"`
OwnerID gid.GID `json:"ownerId"`
}
type CreatePolicyPayload struct {
PolicyEdge *PolicyEdge `json:"policyEdge"`
PolicyVersionEdge *PolicyVersionEdge `json:"policyVersionEdge"`
type CreateDocumentPayload struct {
DocumentEdge *DocumentEdge `json:"documentEdge"`
DocumentVersionEdge *DocumentVersionEdge `json:"documentVersionEdge"`
}
type CreateRiskInput struct {
@@ -222,14 +222,14 @@ type CreateRiskPayload struct {
RiskEdge *RiskEdge `json:"riskEdge"`
}
type CreateRiskPolicyMappingInput struct {
type CreateRiskDocumentMappingInput struct {
RiskID gid.GID `json:"riskId"`
PolicyID gid.GID `json:"policyId"`
DocumentID gid.GID `json:"documentId"`
}
type CreateRiskPolicyMappingPayload struct {
type CreateRiskDocumentMappingPayload struct {
RiskEdge *RiskEdge `json:"riskEdge"`
PolicyEdge *PolicyEdge `json:"policyEdge"`
DocumentEdge *DocumentEdge `json:"documentEdge"`
}
type CreateTaskInput struct {
@@ -294,14 +294,14 @@ type DeleteControlMeasureMappingPayload struct {
DeletedMeasureID gid.GID `json:"deletedMeasureId"`
}
type DeleteControlPolicyMappingInput struct {
type DeleteControlDocumentMappingInput struct {
ControlID gid.GID `json:"controlId"`
PolicyID gid.GID `json:"policyId"`
DocumentID gid.GID `json:"documentId"`
}
type DeleteControlPolicyMappingPayload struct {
type DeleteControlDocumentMappingPayload struct {
DeletedControlID gid.GID `json:"deletedControlId"`
DeletedPolicyID gid.GID `json:"deletedPolicyId"`
DeletedDocumentID gid.GID `json:"deletedDocumentId"`
}
type DeleteEvidenceInput struct {
@@ -344,12 +344,12 @@ type DeletePeoplePayload struct {
DeletedPeopleID gid.GID `json:"deletedPeopleId"`
}
type DeletePolicyInput struct {
PolicyID gid.GID `json:"policyId"`
type DeleteDocumentInput struct {
DocumentID gid.GID `json:"documentId"`
}
type DeletePolicyPayload struct {
DeletedPolicyID gid.GID `json:"deletedPolicyId"`
type DeleteDocumentPayload struct {
DeletedDocumentID gid.GID `json:"deletedDocumentId"`
}
type DeleteRiskInput struct {
@@ -370,14 +370,14 @@ type DeleteRiskPayload struct {
DeletedRiskID gid.GID `json:"deletedRiskId"`
}
type DeleteRiskPolicyMappingInput struct {
type DeleteRiskDocumentMappingInput struct {
RiskID gid.GID `json:"riskId"`
PolicyID gid.GID `json:"policyId"`
DocumentID gid.GID `json:"documentId"`
}
type DeleteRiskPolicyMappingPayload struct {
type DeleteRiskDocumentMappingPayload struct {
DeletedRiskID gid.GID `json:"deletedRiskId"`
DeletedPolicyID gid.GID `json:"deletedPolicyId"`
DeletedDocumentID gid.GID `json:"deletedDocumentId"`
}
type DeleteTaskInput struct {
@@ -542,7 +542,7 @@ type Organization struct {
Frameworks *FrameworkConnection `json:"frameworks"`
Vendors *VendorConnection `json:"vendors"`
Peoples *PeopleConnection `json:"peoples"`
Policies *PolicyConnection `json:"policies"`
Documents *DocumentConnection `json:"documents"`
Measures *MeasureConnection `json:"measures"`
Risks *RiskConnection `json:"risks"`
Tasks *TaskConnection `json:"tasks"`
@@ -601,67 +601,67 @@ type PeopleEdge struct {
Node *People `json:"node"`
}
type Policy struct {
type Document struct {
ID gid.GID `json:"id"`
Title string `json:"title"`
Description string `json:"description"`
CurrentPublishedVersion *int `json:"currentPublishedVersion,omitempty"`
Owner *People `json:"owner"`
Organization *Organization `json:"organization"`
Versions *PolicyVersionConnection `json:"versions"`
Versions *DocumentVersionConnection `json:"versions"`
Controls *ControlConnection `json:"controls"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
func (Policy) IsNode() {}
func (this Policy) GetID() gid.GID { return this.ID }
func (Document) IsNode() {}
func (this Document) GetID() gid.GID { return this.ID }
type PolicyConnection struct {
Edges []*PolicyEdge `json:"edges"`
type DocumentConnection struct {
Edges []*DocumentEdge `json:"edges"`
PageInfo *PageInfo `json:"pageInfo"`
}
type PolicyEdge struct {
type DocumentEdge struct {
Cursor page.CursorKey `json:"cursor"`
Node *Policy `json:"node"`
Node *Document `json:"node"`
}
type PolicyVersion struct {
type DocumentVersion struct {
ID gid.GID `json:"id"`
Policy *Policy `json:"policy"`
Status coredata.PolicyStatus `json:"status"`
Document *Document `json:"document"`
Status coredata.DocumentStatus `json:"status"`
Version int `json:"version"`
Content string `json:"content"`
Changelog string `json:"changelog"`
Signatures *PolicyVersionSignatureConnection `json:"signatures"`
Signatures *DocumentVersionSignatureConnection `json:"signatures"`
PublishedBy *People `json:"publishedBy,omitempty"`
PublishedAt *time.Time `json:"publishedAt,omitempty"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
func (PolicyVersion) IsNode() {}
func (this PolicyVersion) GetID() gid.GID { return this.ID }
func (DocumentVersion) IsNode() {}
func (this DocumentVersion) GetID() gid.GID { return this.ID }
type PolicyVersionConnection struct {
Edges []*PolicyVersionEdge `json:"edges"`
type DocumentVersionConnection struct {
Edges []*DocumentVersionEdge `json:"edges"`
PageInfo *PageInfo `json:"pageInfo"`
}
type PolicyVersionEdge struct {
type DocumentVersionEdge struct {
Cursor page.CursorKey `json:"cursor"`
Node *PolicyVersion `json:"node"`
Node *DocumentVersion `json:"node"`
}
type PolicyVersionFilter struct {
Status *coredata.PolicyStatus `json:"status,omitempty"`
type DocumentVersionFilter struct {
Status *coredata.DocumentStatus `json:"status,omitempty"`
}
type PolicyVersionSignature struct {
type DocumentVersionSignature struct {
ID gid.GID `json:"id"`
PolicyVersion *PolicyVersion `json:"policyVersion"`
State coredata.PolicyVersionSignatureState `json:"state"`
DocumentVersion *DocumentVersion `json:"documentVersion"`
State coredata.DocumentVersionSignatureState `json:"state"`
SignedBy *People `json:"signedBy"`
SignedAt *time.Time `json:"signedAt,omitempty"`
RequestedAt time.Time `json:"requestedAt"`
@@ -670,31 +670,31 @@ type PolicyVersionSignature struct {
UpdatedAt time.Time `json:"updatedAt"`
}
func (PolicyVersionSignature) IsNode() {}
func (this PolicyVersionSignature) GetID() gid.GID { return this.ID }
func (DocumentVersionSignature) IsNode() {}
func (this DocumentVersionSignature) GetID() gid.GID { return this.ID }
type PolicyVersionSignatureConnection struct {
Edges []*PolicyVersionSignatureEdge `json:"edges"`
type DocumentVersionSignatureConnection struct {
Edges []*DocumentVersionSignatureEdge `json:"edges"`
PageInfo *PageInfo `json:"pageInfo"`
}
type PolicyVersionSignatureEdge struct {
type DocumentVersionSignatureEdge struct {
Cursor page.CursorKey `json:"cursor"`
Node *PolicyVersionSignature `json:"node"`
Node *DocumentVersionSignature `json:"node"`
}
type PolicyVersionSignatureOrder struct {
Field coredata.PolicyVersionSignatureOrderField `json:"field"`
type DocumentVersionSignatureOrder struct {
Field coredata.DocumentVersionSignatureOrderField `json:"field"`
Direction page.OrderDirection `json:"direction"`
}
type PublishPolicyVersionInput struct {
PolicyID gid.GID `json:"policyId"`
type PublishDocumentVersionInput struct {
DocumentID gid.GID `json:"documentId"`
}
type PublishPolicyVersionPayload struct {
PolicyVersion *PolicyVersion `json:"policyVersion"`
Policy *Policy `json:"policy"`
type PublishDocumentVersionPayload struct {
DocumentVersion *DocumentVersion `json:"documentVersion"`
Document *Document `json:"document"`
}
type Query struct {
@@ -721,12 +721,12 @@ type RequestEvidencePayload struct {
}
type RequestSignatureInput struct {
PolicyVersionID gid.GID `json:"policyVersionId"`
DocumentVersionID gid.GID `json:"documentVersionId"`
SignatoryID gid.GID `json:"signatoryId"`
}
type RequestSignaturePayload struct {
PolicyVersionSignatureEdge *PolicyVersionSignatureEdge `json:"policyVersionSignatureEdge"`
DocumentVersionSignatureEdge *DocumentVersionSignatureEdge `json:"documentVersionSignatureEdge"`
}
type Risk struct {
@@ -745,7 +745,7 @@ type Risk struct {
Owner *People `json:"owner,omitempty"`
Organization *Organization `json:"organization"`
Measures *MeasureConnection `json:"measures"`
Policies *PolicyConnection `json:"policies"`
Documents *DocumentConnection `json:"documents"`
Controls *ControlConnection `json:"controls"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
@@ -859,7 +859,7 @@ type UpdatePeoplePayload struct {
People *People `json:"people"`
}
type UpdatePolicyInput struct {
type UpdateDocumentInput struct {
ID gid.GID `json:"id"`
Title *string `json:"title,omitempty"`
Content *string `json:"content,omitempty"`
@@ -867,17 +867,17 @@ type UpdatePolicyInput struct {
CreatedBy *gid.GID `json:"createdBy,omitempty"`
}
type UpdatePolicyPayload struct {
Policy *Policy `json:"policy"`
type UpdateDocumentPayload struct {
Document *Document `json:"document"`
}
type UpdatePolicyVersionInput struct {
PolicyVersionID gid.GID `json:"policyVersionId"`
type UpdateDocumentVersionInput struct {
DocumentVersionID gid.GID `json:"documentVersionId"`
Content string `json:"content"`
}
type UpdatePolicyVersionPayload struct {
PolicyVersion *PolicyVersion `json:"policyVersion"`
type UpdateDocumentVersionPayload struct {
DocumentVersion *DocumentVersion `json:"documentVersion"`
}
type UpdateRiskInput struct {

View File

@@ -62,16 +62,16 @@ func (r *controlResolver) Measures(ctx context.Context, obj *types.Control, firs
return types.NewMeasureConnection(page), nil
}
// Policies is the resolver for the policies field.
func (r *controlResolver) Policies(ctx context.Context, obj *types.Control, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.PolicyOrderBy) (*types.PolicyConnection, error) {
// Documents is the resolver for the documents field.
func (r *controlResolver) Documents(ctx context.Context, obj *types.Control, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.DocumentOrderBy) (*types.DocumentConnection, error) {
svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.PolicyOrderField]{
Field: coredata.PolicyOrderFieldCreatedAt,
pageOrderBy := page.OrderBy[coredata.DocumentOrderField]{
Field: coredata.DocumentOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.PolicyOrderField]{
pageOrderBy = page.OrderBy[coredata.DocumentOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
@@ -79,12 +79,12 @@ func (r *controlResolver) Policies(ctx context.Context, obj *types.Control, firs
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
page, err := svc.Policies.ListForControlID(ctx, obj.ID, cursor)
page, err := svc.Documents.ListForControlID(ctx, obj.ID, cursor)
if err != nil {
return nil, fmt.Errorf("cannot list policies: %w", err)
return nil, fmt.Errorf("cannot list documents: %w", err)
}
return types.NewPolicyConnection(page), nil
return types.NewDocumentConnection(page), nil
}
// FileURL is the resolver for the fileUrl field.
@@ -730,18 +730,18 @@ func (r *mutationResolver) CreateControlMeasureMapping(ctx context.Context, inpu
}, nil
}
// CreateControlPolicyMapping is the resolver for the createControlPolicyMapping field.
func (r *mutationResolver) CreateControlPolicyMapping(ctx context.Context, input types.CreateControlPolicyMappingInput) (*types.CreateControlPolicyMappingPayload, error) {
svc := GetTenantService(ctx, r.proboSvc, input.PolicyID.TenantID())
// CreateControlDocumentMapping is the resolver for the createControlDocumentMapping field.
func (r *mutationResolver) CreateControlDocumentMapping(ctx context.Context, input types.CreateControlDocumentMappingInput) (*types.CreateControlDocumentMappingPayload, error) {
svc := GetTenantService(ctx, r.proboSvc, input.DocumentID.TenantID())
control, policy, err := svc.Controls.CreatePolicyMapping(ctx, input.ControlID, input.PolicyID)
control, document, err := svc.Controls.CreateDocumentMapping(ctx, input.ControlID, input.DocumentID)
if err != nil {
panic(fmt.Errorf("cannot create control policy mapping: %w", err))
panic(fmt.Errorf("cannot create control document mapping: %w", err))
}
return &types.CreateControlPolicyMappingPayload{
ControlEdge: types.NewControlEdge(control, coredata.ControlOrderFieldCreatedAt),
PolicyEdge: types.NewPolicyEdge(policy, coredata.PolicyOrderFieldTitle),
return &types.CreateControlDocumentMappingPayload{
ControlEdge: types.NewControlEdge(control, coredata.ControlOrderFieldCreatedAt),
DocumentEdge: types.NewDocumentEdge(document, coredata.DocumentOrderFieldTitle),
}, nil
}
@@ -760,18 +760,18 @@ func (r *mutationResolver) DeleteControlMeasureMapping(ctx context.Context, inpu
}, nil
}
// DeleteControlPolicyMapping is the resolver for the deleteControlPolicyMapping field.
func (r *mutationResolver) DeleteControlPolicyMapping(ctx context.Context, input types.DeleteControlPolicyMappingInput) (*types.DeleteControlPolicyMappingPayload, error) {
svc := GetTenantService(ctx, r.proboSvc, input.PolicyID.TenantID())
// DeleteControlDocumentMapping is the resolver for the deleteControlDocumentMapping field.
func (r *mutationResolver) DeleteControlDocumentMapping(ctx context.Context, input types.DeleteControlDocumentMappingInput) (*types.DeleteControlDocumentMappingPayload, error) {
svc := GetTenantService(ctx, r.proboSvc, input.DocumentID.TenantID())
control, policy, err := svc.Controls.DeletePolicyMapping(ctx, input.ControlID, input.PolicyID)
control, document, err := svc.Controls.DeleteDocumentMapping(ctx, input.ControlID, input.DocumentID)
if err != nil {
panic(fmt.Errorf("cannot delete control policy mapping: %w", err))
panic(fmt.Errorf("cannot delete control document mapping: %w", err))
}
return &types.DeleteControlPolicyMappingPayload{
DeletedControlID: control.ID,
DeletedPolicyID: policy.ID,
return &types.DeleteControlDocumentMappingPayload{
DeletedControlID: control.ID,
DeletedDocumentID: document.ID,
}, nil
}
@@ -959,33 +959,33 @@ func (r *mutationResolver) DeleteRiskMeasureMapping(ctx context.Context, input t
}, nil
}
// CreateRiskPolicyMapping is the resolver for the createRiskPolicyMapping field.
func (r *mutationResolver) CreateRiskPolicyMapping(ctx context.Context, input types.CreateRiskPolicyMappingInput) (*types.CreateRiskPolicyMappingPayload, error) {
// CreateRiskDocumentMapping is the resolver for the createRiskDocumentMapping field.
func (r *mutationResolver) CreateRiskDocumentMapping(ctx context.Context, input types.CreateRiskDocumentMappingInput) (*types.CreateRiskDocumentMappingPayload, error) {
svc := GetTenantService(ctx, r.proboSvc, input.RiskID.TenantID())
risk, policy, err := svc.Risks.CreatePolicyMapping(ctx, input.RiskID, input.PolicyID)
risk, document, err := svc.Risks.CreateDocumentMapping(ctx, input.RiskID, input.DocumentID)
if err != nil {
panic(fmt.Errorf("cannot create risk policy mapping: %w", err))
panic(fmt.Errorf("cannot create risk document mapping: %w", err))
}
return &types.CreateRiskPolicyMappingPayload{
RiskEdge: types.NewRiskEdge(risk, coredata.RiskOrderFieldCreatedAt),
PolicyEdge: types.NewPolicyEdge(policy, coredata.PolicyOrderFieldTitle),
return &types.CreateRiskDocumentMappingPayload{
RiskEdge: types.NewRiskEdge(risk, coredata.RiskOrderFieldCreatedAt),
DocumentEdge: types.NewDocumentEdge(document, coredata.DocumentOrderFieldTitle),
}, nil
}
// DeleteRiskPolicyMapping is the resolver for the deleteRiskPolicyMapping field.
func (r *mutationResolver) DeleteRiskPolicyMapping(ctx context.Context, input types.DeleteRiskPolicyMappingInput) (*types.DeleteRiskPolicyMappingPayload, error) {
// DeleteRiskDocumentMapping is the resolver for the deleteRiskDocumentMapping field.
func (r *mutationResolver) DeleteRiskDocumentMapping(ctx context.Context, input types.DeleteRiskDocumentMappingInput) (*types.DeleteRiskDocumentMappingPayload, error) {
svc := GetTenantService(ctx, r.proboSvc, input.RiskID.TenantID())
risk, policy, err := svc.Risks.DeletePolicyMapping(ctx, input.RiskID, input.PolicyID)
risk, document, err := svc.Risks.DeleteDocumentMapping(ctx, input.RiskID, input.DocumentID)
if err != nil {
panic(fmt.Errorf("cannot delete risk policy mapping: %w", err))
panic(fmt.Errorf("cannot delete risk document mapping: %w", err))
}
return &types.DeleteRiskPolicyMappingPayload{
DeletedRiskID: risk.ID,
DeletedPolicyID: policy.ID,
return &types.DeleteRiskDocumentMappingPayload{
DeletedRiskID: risk.ID,
DeletedDocumentID: document.ID,
}, nil
}
@@ -1139,8 +1139,8 @@ func (r *mutationResolver) DeleteVendorComplianceReport(ctx context.Context, inp
}, nil
}
// CreatePolicy is the resolver for the createPolicy field.
func (r *mutationResolver) CreatePolicy(ctx context.Context, input types.CreatePolicyInput) (*types.CreatePolicyPayload, error) {
// CreateDocument is the resolver for the createDocument field.
func (r *mutationResolver) CreateDocument(ctx context.Context, input types.CreateDocumentInput) (*types.CreateDocumentPayload, error) {
svc := GetTenantService(ctx, r.proboSvc, input.OrganizationID.TenantID())
user := UserFromContext(ctx)
@@ -1149,9 +1149,9 @@ func (r *mutationResolver) CreatePolicy(ctx context.Context, input types.CreateP
panic(fmt.Errorf("cannot get people: %w", err))
}
policy, policyVersion, err := svc.Policies.Create(
document, documentVersion, err := svc.Documents.Create(
ctx,
probo.CreatePolicyRequest{
probo.CreateDocumentRequest{
OrganizationID: input.OrganizationID,
Title: input.Title,
OwnerID: input.OwnerID,
@@ -1160,32 +1160,32 @@ func (r *mutationResolver) CreatePolicy(ctx context.Context, input types.CreateP
},
)
if err != nil {
panic(fmt.Errorf("cannot create policy: %w", err))
panic(fmt.Errorf("cannot create document: %w", err))
}
return &types.CreatePolicyPayload{
PolicyEdge: types.NewPolicyEdge(policy, coredata.PolicyOrderFieldTitle),
PolicyVersionEdge: types.NewPolicyVersionEdge(policyVersion, coredata.PolicyVersionOrderFieldCreatedAt),
return &types.CreateDocumentPayload{
DocumentEdge: types.NewDocumentEdge(document, coredata.DocumentOrderFieldTitle),
DocumentVersionEdge: types.NewDocumentVersionEdge(documentVersion, coredata.DocumentVersionOrderFieldCreatedAt),
}, nil
}
// DeletePolicy is the resolver for the deletePolicy field.
func (r *mutationResolver) DeletePolicy(ctx context.Context, input types.DeletePolicyInput) (*types.DeletePolicyPayload, error) {
svc := GetTenantService(ctx, r.proboSvc, input.PolicyID.TenantID())
// DeleteDocument is the resolver for the deleteDocument field.
func (r *mutationResolver) DeleteDocument(ctx context.Context, input types.DeleteDocumentInput) (*types.DeleteDocumentPayload, error) {
svc := GetTenantService(ctx, r.proboSvc, input.DocumentID.TenantID())
err := svc.Policies.Delete(ctx, input.PolicyID)
err := svc.Documents.Delete(ctx, input.DocumentID)
if err != nil {
panic(fmt.Errorf("cannot delete policy: %w", err))
panic(fmt.Errorf("cannot delete document: %w", err))
}
return &types.DeletePolicyPayload{
DeletedPolicyID: input.PolicyID,
return &types.DeleteDocumentPayload{
DeletedDocumentID: input.DocumentID,
}, nil
}
// PublishPolicyVersion is the resolver for the publishPolicyVersion field.
func (r *mutationResolver) PublishPolicyVersion(ctx context.Context, input types.PublishPolicyVersionInput) (*types.PublishPolicyVersionPayload, error) {
svc := GetTenantService(ctx, r.proboSvc, input.PolicyID.TenantID())
// PublishDocumentVersion is the resolver for the publishDocumentVersion field.
func (r *mutationResolver) PublishDocumentVersion(ctx context.Context, input types.PublishDocumentVersionInput) (*types.PublishDocumentVersionPayload, error) {
svc := GetTenantService(ctx, r.proboSvc, input.DocumentID.TenantID())
user := UserFromContext(ctx)
people, err := svc.Peoples.GetByUserID(ctx, user.ID)
@@ -1193,20 +1193,20 @@ func (r *mutationResolver) PublishPolicyVersion(ctx context.Context, input types
panic(fmt.Errorf("cannot get people: %w", err))
}
policy, policyVersion, err := svc.Policies.PublishVersion(ctx, input.PolicyID, people.ID)
document, documentVersion, err := svc.Documents.PublishVersion(ctx, input.DocumentID, people.ID)
if err != nil {
panic(fmt.Errorf("cannot publish policy version: %w", err))
panic(fmt.Errorf("cannot publish document version: %w", err))
}
return &types.PublishPolicyVersionPayload{
PolicyVersion: types.NewPolicyVersion(policyVersion),
Policy: types.NewPolicy(policy),
return &types.PublishDocumentVersionPayload{
DocumentVersion: types.NewDocumentVersion(documentVersion),
Document: types.NewDocument(document),
}, nil
}
// CreateDraftPolicyVersion is the resolver for the createDraftPolicyVersion field.
func (r *mutationResolver) CreateDraftPolicyVersion(ctx context.Context, input types.CreateDraftPolicyVersionInput) (*types.CreateDraftPolicyVersionPayload, error) {
svc := GetTenantService(ctx, r.proboSvc, input.PolicyID.TenantID())
// CreateDraftDocumentVersion is the resolver for the createDraftDocumentVersion field.
func (r *mutationResolver) CreateDraftDocumentVersion(ctx context.Context, input types.CreateDraftDocumentVersionInput) (*types.CreateDraftDocumentVersionPayload, error) {
svc := GetTenantService(ctx, r.proboSvc, input.DocumentID.TenantID())
user := UserFromContext(ctx)
people, err := svc.Peoples.GetByUserID(ctx, user.ID)
@@ -1214,36 +1214,36 @@ func (r *mutationResolver) CreateDraftPolicyVersion(ctx context.Context, input t
panic(fmt.Errorf("cannot get people: %w", err))
}
policyVersion, err := svc.Policies.CreateDraft(ctx, input.PolicyID, people.ID)
documentVersion, err := svc.Documents.CreateDraft(ctx, input.DocumentID, people.ID)
if err != nil {
panic(fmt.Errorf("cannot create draft policy version: %w", err))
panic(fmt.Errorf("cannot create draft document version: %w", err))
}
return &types.CreateDraftPolicyVersionPayload{
PolicyVersionEdge: types.NewPolicyVersionEdge(policyVersion, coredata.PolicyVersionOrderFieldCreatedAt),
return &types.CreateDraftDocumentVersionPayload{
DocumentVersionEdge: types.NewDocumentVersionEdge(documentVersion, coredata.DocumentVersionOrderFieldCreatedAt),
}, nil
}
// UpdatePolicyVersion is the resolver for the updatePolicyVersion field.
func (r *mutationResolver) UpdatePolicyVersion(ctx context.Context, input types.UpdatePolicyVersionInput) (*types.UpdatePolicyVersionPayload, error) {
svc := GetTenantService(ctx, r.proboSvc, input.PolicyVersionID.TenantID())
// UpdateDocumentVersion is the resolver for the updateDocumentVersion field.
func (r *mutationResolver) UpdateDocumentVersion(ctx context.Context, input types.UpdateDocumentVersionInput) (*types.UpdateDocumentVersionPayload, error) {
svc := GetTenantService(ctx, r.proboSvc, input.DocumentVersionID.TenantID())
policyVersion, err := svc.Policies.UpdateVersion(ctx, probo.UpdatePolicyVersionRequest{
ID: input.PolicyVersionID,
documentVersion, err := svc.Documents.UpdateVersion(ctx, probo.UpdateDocumentVersionRequest{
ID: input.DocumentVersionID,
Content: input.Content,
})
if err != nil {
panic(fmt.Errorf("cannot update policy version: %w", err))
panic(fmt.Errorf("cannot update document version: %w", err))
}
return &types.UpdatePolicyVersionPayload{
PolicyVersion: types.NewPolicyVersion(policyVersion),
return &types.UpdateDocumentVersionPayload{
DocumentVersion: types.NewDocumentVersion(documentVersion),
}, nil
}
// RequestSignature is the resolver for the requestSignature field.
func (r *mutationResolver) RequestSignature(ctx context.Context, input types.RequestSignatureInput) (*types.RequestSignaturePayload, error) {
svc := GetTenantService(ctx, r.proboSvc, input.PolicyVersionID.TenantID())
svc := GetTenantService(ctx, r.proboSvc, input.DocumentVersionID.TenantID())
user := UserFromContext(ctx)
@@ -1252,12 +1252,12 @@ func (r *mutationResolver) RequestSignature(ctx context.Context, input types.Req
panic(fmt.Errorf("cannot get people: %w", err))
}
policyVersionSignature, err := svc.Policies.RequestSignature(
documentVersionSignature, err := svc.Documents.RequestSignature(
ctx,
probo.RequestSignatureRequest{
PolicyVersionID: input.PolicyVersionID,
RequestedBy: people.ID,
Signatory: input.SignatoryID,
DocumentVersionID: input.DocumentVersionID,
RequestedBy: people.ID,
Signatory: input.SignatoryID,
},
)
if err != nil {
@@ -1265,7 +1265,7 @@ func (r *mutationResolver) RequestSignature(ctx context.Context, input types.Req
}
return &types.RequestSignaturePayload{
PolicyVersionSignatureEdge: types.NewPolicyVersionSignatureEdge(policyVersionSignature, coredata.PolicyVersionSignatureOrderFieldCreatedAt),
DocumentVersionSignatureEdge: types.NewDocumentVersionSignatureEdge(documentVersionSignature, coredata.DocumentVersionSignatureOrderFieldCreatedAt),
}, nil
}
@@ -1273,7 +1273,7 @@ func (r *mutationResolver) RequestSignature(ctx context.Context, input types.Req
func (r *mutationResolver) SendSigningNotifications(ctx context.Context, input types.SendSigningNotificationsInput) (*types.SendSigningNotificationsPayload, error) {
svc := GetTenantService(ctx, r.proboSvc, input.OrganizationID.TenantID())
err := svc.Policies.SendSigningNotifications(ctx, input.OrganizationID)
err := svc.Documents.SendSigningNotifications(ctx, input.OrganizationID)
if err != nil {
panic(fmt.Errorf("cannot send signing notifications: %w", err))
}
@@ -1467,16 +1467,16 @@ func (r *organizationResolver) Peoples(ctx context.Context, obj *types.Organizat
return types.NewPeopleConnection(page), nil
}
// Policies is the resolver for the policies field.
func (r *organizationResolver) Policies(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.PolicyOrderBy) (*types.PolicyConnection, error) {
// Documents is the resolver for the documents field.
func (r *organizationResolver) Documents(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.DocumentOrderBy) (*types.DocumentConnection, error) {
svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.PolicyOrderField]{
Field: coredata.PolicyOrderFieldTitle,
pageOrderBy := page.OrderBy[coredata.DocumentOrderField]{
Field: coredata.DocumentOrderFieldTitle,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.PolicyOrderField]{
pageOrderBy = page.OrderBy[coredata.DocumentOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
@@ -1484,12 +1484,12 @@ func (r *organizationResolver) Policies(ctx context.Context, obj *types.Organiza
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
page, err := svc.Policies.ListByOrganizationID(ctx, obj.ID, cursor)
page, err := svc.Documents.ListByOrganizationID(ctx, obj.ID, cursor)
if err != nil {
panic(fmt.Errorf("cannot list organization policies: %w", err))
panic(fmt.Errorf("cannot list organization documents: %w", err))
}
return types.NewPolicyConnection(page), nil
return types.NewDocumentConnection(page), nil
}
// Measures is the resolver for the measures field.
@@ -1568,16 +1568,16 @@ func (r *organizationResolver) Tasks(ctx context.Context, obj *types.Organizatio
}
// Owner is the resolver for the owner field.
func (r *policyResolver) Owner(ctx context.Context, obj *types.Policy) (*types.People, error) {
func (r *documentResolver) Owner(ctx context.Context, obj *types.Document) (*types.People, error) {
svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID())
policy, err := svc.Policies.Get(ctx, obj.ID)
document, err := svc.Documents.Get(ctx, obj.ID)
if err != nil {
panic(fmt.Errorf("cannot get policy: %w", err))
panic(fmt.Errorf("cannot get document: %w", err))
}
// Get the owner
owner, err := svc.Peoples.Get(ctx, policy.OwnerID)
owner, err := svc.Peoples.Get(ctx, document.OwnerID)
if err != nil {
panic(fmt.Errorf("cannot get owner: %w", err))
}
@@ -1586,15 +1586,15 @@ func (r *policyResolver) Owner(ctx context.Context, obj *types.Policy) (*types.P
}
// Organization is the resolver for the organization field.
func (r *policyResolver) Organization(ctx context.Context, obj *types.Policy) (*types.Organization, error) {
func (r *documentResolver) Organization(ctx context.Context, obj *types.Document) (*types.Organization, error) {
svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID())
policy, err := svc.Policies.Get(ctx, obj.ID)
document, err := svc.Documents.Get(ctx, obj.ID)
if err != nil {
panic(fmt.Errorf("cannot get policy: %w", err))
panic(fmt.Errorf("cannot get document: %w", err))
}
organization, err := svc.Organizations.Get(ctx, policy.OrganizationID)
organization, err := svc.Organizations.Get(ctx, document.OrganizationID)
if err != nil {
panic(fmt.Errorf("cannot get organization: %w", err))
}
@@ -1603,15 +1603,15 @@ func (r *policyResolver) Organization(ctx context.Context, obj *types.Policy) (*
}
// Versions is the resolver for the versions field.
func (r *policyResolver) Versions(ctx context.Context, obj *types.Policy, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.PolicyVersionOrderBy, filter *types.PolicyVersionFilter) (*types.PolicyVersionConnection, error) {
func (r *documentResolver) Versions(ctx context.Context, obj *types.Document, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.DocumentVersionOrderBy, filter *types.DocumentVersionFilter) (*types.DocumentVersionConnection, error) {
svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.PolicyVersionOrderField]{
Field: coredata.PolicyVersionOrderFieldCreatedAt,
pageOrderBy := page.OrderBy[coredata.DocumentVersionOrderField]{
Field: coredata.DocumentVersionOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.PolicyVersionOrderField]{
pageOrderBy = page.OrderBy[coredata.DocumentVersionOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
@@ -1619,16 +1619,16 @@ func (r *policyResolver) Versions(ctx context.Context, obj *types.Policy, first
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
page, err := svc.Policies.ListVersions(ctx, obj.ID, cursor)
page, err := svc.Documents.ListVersions(ctx, obj.ID, cursor)
if err != nil {
panic(fmt.Errorf("cannot list policy versions: %w", err))
panic(fmt.Errorf("cannot list document versions: %w", err))
}
return types.NewPolicyVersionConnection(page), nil
return types.NewDocumentVersionConnection(page), nil
}
// Controls is the resolver for the controls field.
func (r *policyResolver) Controls(ctx context.Context, obj *types.Policy, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy) (*types.ControlConnection, error) {
func (r *documentResolver) Controls(ctx context.Context, obj *types.Document, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy) (*types.ControlConnection, error) {
svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.ControlOrderField]{
@@ -1644,41 +1644,41 @@ func (r *policyResolver) Controls(ctx context.Context, obj *types.Policy, first
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
page, err := svc.Controls.ListForPolicyID(ctx, obj.ID, cursor)
page, err := svc.Controls.ListForDocumentID(ctx, obj.ID, cursor)
if err != nil {
panic(fmt.Errorf("cannot list policy controls: %w", err))
panic(fmt.Errorf("cannot list document controls: %w", err))
}
return types.NewControlConnection(page), nil
}
// Policy is the resolver for the policy field.
func (r *policyVersionResolver) Policy(ctx context.Context, obj *types.PolicyVersion) (*types.Policy, error) {
// Document is the resolver for the document field.
func (r *documentVersionResolver) Document(ctx context.Context, obj *types.DocumentVersion) (*types.Document, error) {
svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID())
policyVersion, err := svc.Policies.GetVersion(ctx, obj.ID)
documentVersion, err := svc.Documents.GetVersion(ctx, obj.ID)
if err != nil {
panic(fmt.Errorf("cannot get policy version: %w", err))
panic(fmt.Errorf("cannot get document version: %w", err))
}
policy, err := svc.Policies.Get(ctx, policyVersion.PolicyID)
document, err := svc.Documents.Get(ctx, documentVersion.DocumentID)
if err != nil {
panic(fmt.Errorf("cannot get policy: %w", err))
panic(fmt.Errorf("cannot get document: %w", err))
}
return types.NewPolicy(policy), nil
return types.NewDocument(document), nil
}
// Signatures is the resolver for the signatures field.
func (r *policyVersionResolver) Signatures(ctx context.Context, obj *types.PolicyVersion, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.PolicyVersionSignatureOrder) (*types.PolicyVersionSignatureConnection, error) {
func (r *documentVersionResolver) Signatures(ctx context.Context, obj *types.DocumentVersion, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.DocumentVersionSignatureOrder) (*types.DocumentVersionSignatureConnection, error) {
svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.PolicyVersionSignatureOrderField]{
Field: coredata.PolicyVersionSignatureOrderFieldCreatedAt,
pageOrderBy := page.OrderBy[coredata.DocumentVersionSignatureOrderField]{
Field: coredata.DocumentVersionSignatureOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.PolicyVersionSignatureOrderField]{
pageOrderBy = page.OrderBy[coredata.DocumentVersionSignatureOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
@@ -1686,28 +1686,28 @@ func (r *policyVersionResolver) Signatures(ctx context.Context, obj *types.Polic
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
page, err := svc.Policies.ListSignatures(ctx, obj.ID, cursor)
page, err := svc.Documents.ListSignatures(ctx, obj.ID, cursor)
if err != nil {
panic(fmt.Errorf("cannot list policy version signatures: %w", err))
panic(fmt.Errorf("cannot list document version signatures: %w", err))
}
return types.NewPolicyVersionSignatureConnection(page), nil
return types.NewDocumentVersionSignatureConnection(page), nil
}
// PublishedBy is the resolver for the publishedBy field.
func (r *policyVersionResolver) PublishedBy(ctx context.Context, obj *types.PolicyVersion) (*types.People, error) {
func (r *documentVersionResolver) PublishedBy(ctx context.Context, obj *types.DocumentVersion) (*types.People, error) {
svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID())
policyVersion, err := svc.Policies.GetVersion(ctx, obj.ID)
documentVersion, err := svc.Documents.GetVersion(ctx, obj.ID)
if err != nil {
panic(fmt.Errorf("cannot get policy version: %w", err))
panic(fmt.Errorf("cannot get document version: %w", err))
}
if policyVersion.PublishedBy == nil {
if documentVersion.PublishedBy == nil {
return nil, nil
}
people, err := svc.Peoples.Get(ctx, *policyVersion.PublishedBy)
people, err := svc.Peoples.Get(ctx, *documentVersion.PublishedBy)
if err != nil {
panic(fmt.Errorf("cannot get people: %w", err))
}
@@ -1715,33 +1715,33 @@ func (r *policyVersionResolver) PublishedBy(ctx context.Context, obj *types.Poli
return types.NewPeople(people), nil
}
// PolicyVersion is the resolver for the policyVersion field.
func (r *policyVersionSignatureResolver) PolicyVersion(ctx context.Context, obj *types.PolicyVersionSignature) (*types.PolicyVersion, error) {
// DocumentVersion is the resolver for the documentVersion field.
func (r *documentVersionSignatureResolver) DocumentVersion(ctx context.Context, obj *types.DocumentVersionSignature) (*types.DocumentVersion, error) {
svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID())
policyVersionSignature, err := svc.Policies.GetVersionSignature(ctx, obj.ID)
documentVersionSignature, err := svc.Documents.GetVersionSignature(ctx, obj.ID)
if err != nil {
panic(fmt.Errorf("cannot get policy version signature: %w", err))
panic(fmt.Errorf("cannot get document version signature: %w", err))
}
policyVersion, err := svc.Policies.GetVersion(ctx, policyVersionSignature.PolicyVersionID)
documentVersion, err := svc.Documents.GetVersion(ctx, documentVersionSignature.DocumentVersionID)
if err != nil {
panic(fmt.Errorf("cannot get policy version: %w", err))
panic(fmt.Errorf("cannot get document version: %w", err))
}
return types.NewPolicyVersion(policyVersion), nil
return types.NewDocumentVersion(documentVersion), nil
}
// SignedBy is the resolver for the signedBy field.
func (r *policyVersionSignatureResolver) SignedBy(ctx context.Context, obj *types.PolicyVersionSignature) (*types.People, error) {
func (r *documentVersionSignatureResolver) SignedBy(ctx context.Context, obj *types.DocumentVersionSignature) (*types.People, error) {
svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID())
policyVersionSignature, err := svc.Policies.GetVersionSignature(ctx, obj.ID)
documentVersionSignature, err := svc.Documents.GetVersionSignature(ctx, obj.ID)
if err != nil {
panic(fmt.Errorf("cannot get policy version signature: %w", err))
panic(fmt.Errorf("cannot get document version signature: %w", err))
}
people, err := svc.Peoples.Get(ctx, policyVersionSignature.SignedBy)
people, err := svc.Peoples.Get(ctx, documentVersionSignature.SignedBy)
if err != nil {
panic(fmt.Errorf("cannot get people: %w", err))
}
@@ -1750,15 +1750,15 @@ func (r *policyVersionSignatureResolver) SignedBy(ctx context.Context, obj *type
}
// RequestedBy is the resolver for the requestedBy field.
func (r *policyVersionSignatureResolver) RequestedBy(ctx context.Context, obj *types.PolicyVersionSignature) (*types.People, error) {
func (r *documentVersionSignatureResolver) RequestedBy(ctx context.Context, obj *types.DocumentVersionSignature) (*types.People, error) {
svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID())
policyVersionSignature, err := svc.Policies.GetVersionSignature(ctx, obj.ID)
documentVersionSignature, err := svc.Documents.GetVersionSignature(ctx, obj.ID)
if err != nil {
panic(fmt.Errorf("cannot get policy version signature: %w", err))
panic(fmt.Errorf("cannot get document version signature: %w", err))
}
people, err := svc.Peoples.Get(ctx, policyVersionSignature.RequestedBy)
people, err := svc.Peoples.Get(ctx, documentVersionSignature.RequestedBy)
if err != nil {
panic(fmt.Errorf("cannot get people: %w", err))
}
@@ -1820,12 +1820,12 @@ func (r *queryResolver) Node(ctx context.Context, id gid.GID) (types.Node, error
}
return types.NewEvidence(evidence), nil
case coredata.PolicyEntityType:
policy, err := svc.Policies.Get(ctx, id)
case coredata.DocumentEntityType:
document, err := svc.Documents.Get(ctx, id)
if err != nil {
panic(fmt.Errorf("cannot get policy: %w", err))
panic(fmt.Errorf("cannot get document: %w", err))
}
return types.NewPolicy(policy), nil
return types.NewDocument(document), nil
case coredata.ControlEntityType:
control, err := svc.Controls.Get(ctx, id)
if err != nil {
@@ -1845,18 +1845,18 @@ func (r *queryResolver) Node(ctx context.Context, id gid.GID) (types.Node, error
panic(fmt.Errorf("cannot get vendor compliance report: %w", err))
}
return types.NewVendorComplianceReport(vendorComplianceReport), nil
case coredata.PolicyVersionEntityType:
policyVersion, err := svc.Policies.GetVersion(ctx, id)
case coredata.DocumentVersionEntityType:
documentVersion, err := svc.Documents.GetVersion(ctx, id)
if err != nil {
panic(fmt.Errorf("cannot get policy version: %w", err))
panic(fmt.Errorf("cannot get document version: %w", err))
}
return types.NewPolicyVersion(policyVersion), nil
case coredata.PolicyVersionSignatureEntityType:
policyVersionSignature, err := svc.Policies.GetVersionSignature(ctx, id)
return types.NewDocumentVersion(documentVersion), nil
case coredata.DocumentVersionSignatureEntityType:
documentVersionSignature, err := svc.Documents.GetVersionSignature(ctx, id)
if err != nil {
panic(fmt.Errorf("cannot get policy version signature: %w", err))
panic(fmt.Errorf("cannot get document version signature: %w", err))
}
return types.NewPolicyVersionSignature(policyVersionSignature), nil
return types.NewDocumentVersionSignature(documentVersionSignature), nil
default:
}
@@ -1937,16 +1937,16 @@ func (r *riskResolver) Measures(ctx context.Context, obj *types.Risk, first *int
return types.NewMeasureConnection(page), nil
}
// Policies is the resolver for the policies field.
func (r *riskResolver) Policies(ctx context.Context, obj *types.Risk, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.PolicyOrderBy) (*types.PolicyConnection, error) {
// Documents is the resolver for the documents field.
func (r *riskResolver) Documents(ctx context.Context, obj *types.Risk, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.DocumentOrderBy) (*types.DocumentConnection, error) {
svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.PolicyOrderField]{
Field: coredata.PolicyOrderFieldCreatedAt,
pageOrderBy := page.OrderBy[coredata.DocumentOrderField]{
Field: coredata.DocumentOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.PolicyOrderField]{
pageOrderBy = page.OrderBy[coredata.DocumentOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
@@ -1954,12 +1954,12 @@ func (r *riskResolver) Policies(ctx context.Context, obj *types.Risk, first *int
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
page, err := svc.Policies.ListForRiskID(ctx, obj.ID, cursor)
page, err := svc.Documents.ListForRiskID(ctx, obj.ID, cursor)
if err != nil {
panic(fmt.Errorf("cannot list risk policies: %w", err))
panic(fmt.Errorf("cannot list risk documents: %w", err))
}
return types.NewPolicyConnection(page), nil
return types.NewDocumentConnection(page), nil
}
// Controls is the resolver for the controls field.
@@ -2283,15 +2283,17 @@ func (r *Resolver) Mutation() schema.MutationResolver { return &mutationResolver
// Organization returns schema.OrganizationResolver implementation.
func (r *Resolver) Organization() schema.OrganizationResolver { return &organizationResolver{r} }
// Policy returns schema.PolicyResolver implementation.
func (r *Resolver) Policy() schema.PolicyResolver { return &policyResolver{r} }
// Document returns schema.DocumentResolver implementation.
func (r *Resolver) Document() schema.DocumentResolver { return &documentResolver{r} }
// PolicyVersion returns schema.PolicyVersionResolver implementation.
func (r *Resolver) PolicyVersion() schema.PolicyVersionResolver { return &policyVersionResolver{r} }
// DocumentVersion returns schema.DocumentVersionResolver implementation.
func (r *Resolver) DocumentVersion() schema.DocumentVersionResolver {
return &documentVersionResolver{r}
}
// PolicyVersionSignature returns schema.PolicyVersionSignatureResolver implementation.
func (r *Resolver) PolicyVersionSignature() schema.PolicyVersionSignatureResolver {
return &policyVersionSignatureResolver{r}
// DocumentVersionSignature returns schema.DocumentVersionSignatureResolver implementation.
func (r *Resolver) DocumentVersionSignature() schema.DocumentVersionSignatureResolver {
return &documentVersionSignatureResolver{r}
}
// Query returns schema.QueryResolver implementation.
@@ -2328,9 +2330,9 @@ type frameworkResolver struct{ *Resolver }
type measureResolver struct{ *Resolver }
type mutationResolver struct{ *Resolver }
type organizationResolver struct{ *Resolver }
type policyResolver struct{ *Resolver }
type policyVersionResolver struct{ *Resolver }
type policyVersionSignatureResolver struct{ *Resolver }
type documentResolver struct{ *Resolver }
type documentVersionResolver struct{ *Resolver }
type documentVersionSignatureResolver struct{ *Resolver }
type queryResolver struct{ *Resolver }
type riskResolver struct{ *Resolver }
type taskResolver struct{ *Resolver }