Add document type to the new ui

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
Sacha Al Himdani
2025-06-12 18:19:58 -07:00
parent 737accc787
commit c657325295
9 changed files with 96 additions and 18 deletions

View File

@@ -5,8 +5,13 @@ export const documentSchema = z.object({
title: z.string(), title: z.string(),
content: z.string(), content: z.string(),
ownerId: z.string(), ownerId: z.string(),
documentType: z.enum(["OTHER", "ISMS", "POLICY"]),
}); });
export const useDocumentForm = () => { export const useDocumentForm = () => {
return useFormWithSchema(documentSchema, {}); return useFormWithSchema(documentSchema, {
defaultValues: {
documentType: "POLICY",
},
});
}; };

View File

@@ -1,5 +1,5 @@
/** /**
* @generated SignedSource<<db2f3c23a3ecbb61ac692a46e93da44a>> * @generated SignedSource<<3318d21bcfef8523d95b650c01bc7973>>
* @lightSyntaxTransform * @lightSyntaxTransform
* @nogrep * @nogrep
*/ */
@@ -146,6 +146,13 @@ return {
"name": "description", "name": "description",
"storageKey": null "storageKey": null
}, },
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "documentType",
"storageKey": null
},
{ {
"alias": null, "alias": null,
"args": null, "args": null,
@@ -333,12 +340,12 @@ return {
] ]
}, },
"params": { "params": {
"cacheID": "d567583accd89d49f0c40fe1bac5ed84", "cacheID": "edc04f86b008ac09088a88a6b7918cbe",
"id": null, "id": null,
"metadata": {}, "metadata": {},
"name": "DocumentGraphListQuery", "name": "DocumentGraphListQuery",
"operationKind": "query", "operationKind": "query",
"text": "query DocumentGraphListQuery(\n $organizationId: ID!\n) {\n organization: node(id: $organizationId) {\n __typename\n id\n ...DocumentsPageListFragment\n }\n}\n\nfragment DocumentsPageListFragment on Organization {\n documents(first: 100) {\n edges {\n node {\n id\n ...DocumentsPageRowFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n}\n\nfragment DocumentsPageRowFragment on Document {\n id\n title\n description\n updatedAt\n owner {\n id\n fullName\n }\n versions(first: 1) {\n edges {\n node {\n id\n status\n signatures(first: 100) {\n edges {\n node {\n id\n state\n }\n }\n }\n }\n }\n }\n}\n" "text": "query DocumentGraphListQuery(\n $organizationId: ID!\n) {\n organization: node(id: $organizationId) {\n __typename\n id\n ...DocumentsPageListFragment\n }\n}\n\nfragment DocumentsPageListFragment on Organization {\n documents(first: 100) {\n edges {\n node {\n id\n ...DocumentsPageRowFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n}\n\nfragment DocumentsPageRowFragment on Document {\n id\n title\n description\n documentType\n updatedAt\n owner {\n id\n fullName\n }\n versions(first: 1) {\n edges {\n node {\n id\n status\n signatures(first: 100) {\n edges {\n node {\n id\n state\n }\n }\n }\n }\n }\n }\n}\n"
} }
}; };
})(); })();

View File

@@ -1,5 +1,5 @@
/** /**
* @generated SignedSource<<91fae4ffa86907530e46f054c89f36c3>> * @generated SignedSource<<5c3e7dba407e37cfc0203138d5db8318>>
* @lightSyntaxTransform * @lightSyntaxTransform
* @nogrep * @nogrep
*/ */
@@ -186,6 +186,13 @@ return {
"name": "title", "name": "title",
"storageKey": null "storageKey": null
}, },
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "documentType",
"storageKey": null
},
{ {
"alias": null, "alias": null,
"args": null, "args": null,
@@ -520,12 +527,12 @@ return {
] ]
}, },
"params": { "params": {
"cacheID": "389866b9db384ce6c9f4e79caab3a6fd", "cacheID": "ad8c2b55a545807ab1fe72c21ed8f9c1",
"id": null, "id": null,
"metadata": {}, "metadata": {},
"name": "DocumentGraphNodeQuery", "name": "DocumentGraphNodeQuery",
"operationKind": "query", "operationKind": "query",
"text": "query DocumentGraphNodeQuery(\n $documentId: ID!\n) {\n node(id: $documentId) {\n __typename\n ... on Document {\n ...DocumentDetailPageDocumentFragment\n }\n id\n }\n}\n\nfragment DocumentControlsTabFragment on Document {\n id\n controls(first: 20) {\n edges {\n node {\n id\n ...LinkedControlsCardFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n}\n\nfragment DocumentDetailPageDocumentFragment on Document {\n id\n title\n owner {\n id\n fullName\n }\n ...DocumentControlsTabFragment\n controlsInfo: controls(first: 0) {\n totalCount\n }\n versions(first: 20) {\n edges {\n node {\n id\n content\n status\n publishedAt\n version\n updatedAt\n signatures(first: 100) {\n edges {\n node {\n id\n state\n signedBy {\n id\n }\n ...DocumentSignaturesDialog_signature\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n ...DocumentVersionHistoryDialogFragment\n ...DocumentSignaturesDialog_version\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n}\n\nfragment DocumentSignaturesDialog_signature on DocumentVersionSignature {\n id\n state\n signedAt\n requestedAt\n signedBy {\n fullName\n primaryEmailAddress\n id\n }\n}\n\nfragment DocumentSignaturesDialog_version on DocumentVersion {\n version\n status\n publishedAt\n updatedAt\n}\n\nfragment DocumentVersionHistoryDialogFragment on DocumentVersion {\n id\n version\n status\n content\n changelog\n publishedAt\n updatedAt\n publishedBy {\n fullName\n id\n }\n}\n\nfragment LinkedControlsCardFragment on Control {\n id\n name\n sectionTitle\n framework {\n id\n name\n }\n}\n" "text": "query DocumentGraphNodeQuery(\n $documentId: ID!\n) {\n node(id: $documentId) {\n __typename\n ... on Document {\n ...DocumentDetailPageDocumentFragment\n }\n id\n }\n}\n\nfragment DocumentControlsTabFragment on Document {\n id\n controls(first: 20) {\n edges {\n node {\n id\n ...LinkedControlsCardFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n}\n\nfragment DocumentDetailPageDocumentFragment on Document {\n id\n title\n documentType\n owner {\n id\n fullName\n }\n ...DocumentControlsTabFragment\n controlsInfo: controls(first: 0) {\n totalCount\n }\n versions(first: 20) {\n edges {\n node {\n id\n content\n status\n publishedAt\n version\n updatedAt\n signatures(first: 100) {\n edges {\n node {\n id\n state\n signedBy {\n id\n }\n ...DocumentSignaturesDialog_signature\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n ...DocumentVersionHistoryDialogFragment\n ...DocumentSignaturesDialog_version\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n}\n\nfragment DocumentSignaturesDialog_signature on DocumentVersionSignature {\n id\n state\n signedAt\n requestedAt\n signedBy {\n fullName\n primaryEmailAddress\n id\n }\n}\n\nfragment DocumentSignaturesDialog_version on DocumentVersion {\n version\n status\n publishedAt\n updatedAt\n}\n\nfragment DocumentVersionHistoryDialogFragment on DocumentVersion {\n id\n version\n status\n content\n changelog\n publishedAt\n updatedAt\n publishedBy {\n fullName\n id\n }\n}\n\nfragment LinkedControlsCardFragment on Control {\n id\n name\n sectionTitle\n framework {\n id\n name\n }\n}\n"
} }
}; };
})(); })();

View File

@@ -37,7 +37,7 @@ import {
import { useOrganizationId } from "/hooks/useOrganizationId"; import { useOrganizationId } from "/hooks/useOrganizationId";
import { Button } from "@probo/ui"; import { Button } from "@probo/ui";
import { useMutationWithToasts } from "/hooks/useMutationWithToasts"; import { useMutationWithToasts } from "/hooks/useMutationWithToasts";
import { sprintf } from "@probo/helpers"; import { sprintf, getDocumentTypeLabel } from "@probo/helpers";
import { Outlet, useNavigate } from "react-router"; import { Outlet, useNavigate } from "react-router";
import UpdateVersionDialog from "./dialogs/UpdateVersionDialog"; import UpdateVersionDialog from "./dialogs/UpdateVersionDialog";
import { useRef } from "react"; import { useRef } from "react";
@@ -52,6 +52,7 @@ const documentFragment = graphql`
fragment DocumentDetailPageDocumentFragment on Document { fragment DocumentDetailPageDocumentFragment on Document {
id id
title title
documentType
owner { owner {
id id
fullName fullName
@@ -266,6 +267,11 @@ export default function DocumentDetailPage(props: Props) {
{document.owner?.fullName} {document.owner?.fullName}
</Badge> </Badge>
</PropertyRow> </PropertyRow>
<PropertyRow label={__("Type")}>
<div className="text-sm text-txt-secondary">
{getDocumentTypeLabel(__, document.documentType)}
</div>
</PropertyRow>
<PropertyRow label={__("Status")}> <PropertyRow label={__("Status")}>
<Badge <Badge
variant={isDraft ? "highlight" : "success"} variant={isDraft ? "highlight" : "success"}

View File

@@ -29,7 +29,7 @@ import {
} from "/hooks/graph/DocumentGraph"; } from "/hooks/graph/DocumentGraph";
import type { DocumentsPageListFragment$key } from "./__generated__/DocumentsPageListFragment.graphql"; import type { DocumentsPageListFragment$key } from "./__generated__/DocumentsPageListFragment.graphql";
import { usePageTitle } from "@probo/hooks"; import { usePageTitle } from "@probo/hooks";
import { sprintf } from "@probo/helpers"; import { sprintf, getDocumentTypeLabel } from "@probo/helpers";
import { CreateDocumentDialog } from "./dialogs/CreateDocumentDialog"; import { CreateDocumentDialog } from "./dialogs/CreateDocumentDialog";
import type { DocumentsPageRowFragment$key } from "./__generated__/DocumentsPageRowFragment.graphql"; import type { DocumentsPageRowFragment$key } from "./__generated__/DocumentsPageRowFragment.graphql";
@@ -71,7 +71,7 @@ export default function DocumentsPage(props: Props) {
<div className="space-y-6"> <div className="space-y-6">
<PageHeader <PageHeader
title={__("Documents")} title={__("Documents")}
description={__("Manage your organization’s documents")} description={__("Manage your organization's documents")}
> >
<CreateDocumentDialog <CreateDocumentDialog
connection={connectionId} connection={connectionId}
@@ -83,6 +83,7 @@ export default function DocumentsPage(props: Props) {
<Tr> <Tr>
<Th>{__("Name")}</Th> <Th>{__("Name")}</Th>
<Th>{__("Status")}</Th> <Th>{__("Status")}</Th>
<Th>{__("Type")}</Th>
<Th>{__("Owner")}</Th> <Th>{__("Owner")}</Th>
<Th>{__("Last update")}</Th> <Th>{__("Last update")}</Th>
<Th>{__("Signatures")}</Th> <Th>{__("Signatures")}</Th>
@@ -109,6 +110,7 @@ const rowFragment = graphql`
id id
title title
description description
documentType
updatedAt updatedAt
owner { owner {
id id
@@ -195,6 +197,9 @@ function DocumentRow({
{isDraft ? __("Draft") : __("Published")} {isDraft ? __("Draft") : __("Published")}
</Badge> </Badge>
</Td> </Td>
<Td>
{getDocumentTypeLabel(__, document.documentType)}
</Td>
<Td> <Td>
<div className="flex gap-2 items-center"> <div className="flex gap-2 items-center">
<Avatar name={document.owner.fullName} /> <Avatar name={document.owner.fullName} />

View File

@@ -1,5 +1,5 @@
/** /**
* @generated SignedSource<<de19bed523a0a3e24807f5e7ecfef466>> * @generated SignedSource<<4318aaecefe001fe6f42e6db26c421b4>>
* @lightSyntaxTransform * @lightSyntaxTransform
* @nogrep * @nogrep
*/ */
@@ -10,12 +10,14 @@
import { ReaderFragment } from 'relay-runtime'; import { ReaderFragment } from 'relay-runtime';
export type DocumentStatus = "DRAFT" | "PUBLISHED"; export type DocumentStatus = "DRAFT" | "PUBLISHED";
export type DocumentType = "ISMS" | "OTHER" | "POLICY";
export type DocumentVersionSignatureState = "REQUESTED" | "SIGNED"; export type DocumentVersionSignatureState = "REQUESTED" | "SIGNED";
import { FragmentRefs } from "relay-runtime"; import { FragmentRefs } from "relay-runtime";
export type DocumentDetailPageDocumentFragment$data = { export type DocumentDetailPageDocumentFragment$data = {
readonly controlsInfo: { readonly controlsInfo: {
readonly totalCount: number; readonly totalCount: number;
}; };
readonly documentType: DocumentType;
readonly id: string; readonly id: string;
readonly owner: { readonly owner: {
readonly fullName: string; readonly fullName: string;
@@ -147,6 +149,13 @@ return {
"name": "title", "name": "title",
"storageKey": null "storageKey": null
}, },
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "documentType",
"storageKey": null
},
{ {
"alias": null, "alias": null,
"args": null, "args": null,
@@ -346,6 +355,6 @@ return {
}; };
})(); })();
(node as any).hash = "ffd58750acd50c91d62a674ba158836c"; (node as any).hash = "da1a4055d701fa9c3e0e0e50df24860f";
export default node; export default node;

View File

@@ -1,5 +1,5 @@
/** /**
* @generated SignedSource<<394470df6f042c06c640caad2df537cc>> * @generated SignedSource<<428295a295761694c38d77aa3476d01f>>
* @lightSyntaxTransform * @lightSyntaxTransform
* @nogrep * @nogrep
*/ */
@@ -10,10 +10,12 @@
import { ReaderFragment } from 'relay-runtime'; import { ReaderFragment } from 'relay-runtime';
export type DocumentStatus = "DRAFT" | "PUBLISHED"; export type DocumentStatus = "DRAFT" | "PUBLISHED";
export type DocumentType = "ISMS" | "OTHER" | "POLICY";
export type DocumentVersionSignatureState = "REQUESTED" | "SIGNED"; export type DocumentVersionSignatureState = "REQUESTED" | "SIGNED";
import { FragmentRefs } from "relay-runtime"; import { FragmentRefs } from "relay-runtime";
export type DocumentsPageRowFragment$data = { export type DocumentsPageRowFragment$data = {
readonly description: string; readonly description: string;
readonly documentType: DocumentType;
readonly id: string; readonly id: string;
readonly owner: { readonly owner: {
readonly fullName: string; readonly fullName: string;
@@ -73,6 +75,13 @@ return {
"name": "description", "name": "description",
"storageKey": null "storageKey": null
}, },
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "documentType",
"storageKey": null
},
{ {
"alias": null, "alias": null,
"args": null, "args": null,
@@ -199,6 +208,6 @@ return {
}; };
})(); })();
(node as any).hash = "b5339e945b607000f177036cad41e0d4"; (node as any).hash = "c8543275dc108732ad2377806efacfc1";
export default node; export default node;

View File

@@ -11,6 +11,7 @@ import {
PropertyRow, PropertyRow,
Textarea, Textarea,
useDialogRef, useDialogRef,
Option,
} from "@probo/ui"; } from "@probo/ui";
import { type ReactNode } from "react"; import { type ReactNode } from "react";
import { graphql } from "relay-runtime"; import { graphql } from "relay-runtime";
@@ -19,6 +20,7 @@ import { useDocumentForm } from "/hooks/forms/useDocumentForm";
import { useMutationWithToasts } from "/hooks/useMutationWithToasts"; import { useMutationWithToasts } from "/hooks/useMutationWithToasts";
import { PeopleSelectField } from "/components/form/PeopleSelectField"; import { PeopleSelectField } from "/components/form/PeopleSelectField";
import type { CreateDocumentDialogMutation } from "./__generated__/CreateDocumentDialogMutation.graphql"; import type { CreateDocumentDialogMutation } from "./__generated__/CreateDocumentDialogMutation.graphql";
import { ControlledField } from "/components/form/ControlledField";
type Props = { type Props = {
trigger?: ReactNode; trigger?: ReactNode;
@@ -60,7 +62,6 @@ export function CreateDocumentDialog({ trigger, connection }: Props) {
input: { input: {
...data, ...data,
organizationId, organizationId,
documentType: "POLICY",
}, },
connections: [connection!], connections: [connection!],
}, },
@@ -110,6 +111,28 @@ export function CreateDocumentDialog({ trigger, connection }: Props) {
</Badge> </Badge>
</PropertyRow> </PropertyRow>
<PropertyRow
id="documentType"
label={__("Type")}
error={errors.documentType?.message}
>
<ControlledField
control={control}
name="documentType"
type="select"
>
<Option value="POLICY">
{__("Policy")}
</Option>
<Option value="ISMS">
{__("ISMS")}
</Option>
<Option value="OTHER">
{__("Other")}
</Option>
</ControlledField>
</PropertyRow>
<PropertyRow <PropertyRow
id="ownerId" id="ownerId"
label={__("Owner")} label={__("Owner")}

View File

@@ -1,5 +1,5 @@
/** /**
* @generated SignedSource<<99814a217713cd574beb65c91802dc74>> * @generated SignedSource<<ce2f4f138b340f04b4e413ecbfff2ea1>>
* @lightSyntaxTransform * @lightSyntaxTransform
* @nogrep * @nogrep
*/ */
@@ -163,6 +163,13 @@ return {
"name": "description", "name": "description",
"storageKey": null "storageKey": null
}, },
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "documentType",
"storageKey": null
},
{ {
"alias": null, "alias": null,
"args": null, "args": null,
@@ -311,12 +318,12 @@ return {
] ]
}, },
"params": { "params": {
"cacheID": "d535d393c2ab2e1a9798d193bcc8c17f", "cacheID": "e60a87953e4157e808e0d4ce4436ed3b",
"id": null, "id": null,
"metadata": {}, "metadata": {},
"name": "CreateDocumentDialogMutation", "name": "CreateDocumentDialogMutation",
"operationKind": "mutation", "operationKind": "mutation",
"text": "mutation CreateDocumentDialogMutation(\n $input: CreateDocumentInput!\n) {\n createDocument(input: $input) {\n documentEdge {\n node {\n id\n ...DocumentsPageRowFragment\n }\n }\n }\n}\n\nfragment DocumentsPageRowFragment on Document {\n id\n title\n description\n updatedAt\n owner {\n id\n fullName\n }\n versions(first: 1) {\n edges {\n node {\n id\n status\n signatures(first: 100) {\n edges {\n node {\n id\n state\n }\n }\n }\n }\n }\n }\n}\n" "text": "mutation CreateDocumentDialogMutation(\n $input: CreateDocumentInput!\n) {\n createDocument(input: $input) {\n documentEdge {\n node {\n id\n ...DocumentsPageRowFragment\n }\n }\n }\n}\n\nfragment DocumentsPageRowFragment on Document {\n id\n title\n description\n documentType\n updatedAt\n owner {\n id\n fullName\n }\n versions(first: 1) {\n edges {\n node {\n id\n status\n signatures(first: 100) {\n edges {\n node {\n id\n state\n }\n }\n }\n }\n }\n }\n}\n"
} }
}; };
})(); })();