Increase number of loaded signature

Signed-off-by: Bryan Frimin <bryan@getprobo.com>
This commit is contained in:
Bryan Frimin
2025-10-16 11:44:51 +02:00
parent 724461d02c
commit 44c3b3ae97
9 changed files with 151 additions and 110 deletions

View File

@@ -47,7 +47,12 @@ import {
} from "@probo/ui";
import { useOrganizationId } from "/hooks/useOrganizationId";
import { useMutationWithToasts } from "/hooks/useMutationWithToasts";
import { getDocumentTypeLabel, sprintf, documentTypes, formatDate } from "@probo/helpers";
import {
getDocumentTypeLabel,
sprintf,
documentTypes,
formatDate,
} from "@probo/helpers";
import {
Link,
Outlet,
@@ -56,7 +61,10 @@ import {
useParams,
} from "react-router";
import UpdateVersionDialog from "./dialogs/UpdateVersionDialog";
import { PdfDownloadDialog, type PdfDownloadDialogRef } from "/components/documents/PdfDownloadDialog";
import {
PdfDownloadDialog,
type PdfDownloadDialogRef,
} from "/components/documents/PdfDownloadDialog";
import { useRef, useState } from "react";
import type { NodeOf } from "/types.ts";
import clsx from "clsx";
@@ -93,7 +101,7 @@ const documentFragment = graphql`
publishedAt
version
updatedAt
signatures(first: 100)
signatures(first: 500)
@connection(key: "DocumentDetailPage_signatures") {
__id
edges {
@@ -227,7 +235,8 @@ export default function DocumentDetailPage(props: Props) {
}
);
const [deleteDocument, isDeleting] = useDeleteDocumentMutation();
const [deleteDraftDocumentVersion, isDeletingDraft] = useDeleteDraftDocumentVersionMutation();
const [deleteDraftDocumentVersion, isDeletingDraft] =
useDeleteDraftDocumentVersionMutation();
const [exportDocumentVersionPDF, isExporting] =
useMutationWithToasts<DocumentDetailPageExportPDFMutation>(
exportDocumentVersionPDFMutation,
@@ -237,24 +246,31 @@ export default function DocumentDetailPage(props: Props) {
}
);
const userEmailData = useLazyLoadQuery<DocumentDetailPageUserEmailQuery>(UserEmailQuery, {});
const defaultEmail = userEmailData.viewer.user.email;
const [updateDocument, isUpdatingDocument] = useMutationWithToasts<DocumentDetailPageUpdateMutation>(
updateDocumentMutation,
{
successMessage: __("Document updated successfully."),
errorMessage: __("Failed to update document. Please try again."),
}
const userEmailData = useLazyLoadQuery<DocumentDetailPageUserEmailQuery>(
UserEmailQuery,
{}
);
const defaultEmail = userEmailData.viewer.user.email;
const [updateDocument, isUpdatingDocument] =
useMutationWithToasts<DocumentDetailPageUpdateMutation>(
updateDocumentMutation,
{
successMessage: __("Document updated successfully."),
errorMessage: __("Failed to update document. Please try again."),
}
);
const versionConnectionId = document.versions.__id;
const { register, control, handleSubmit, reset } = useFormWithSchema(documentUpdateSchema, {
defaultValues: {
title: document.title,
ownerId: document.owner?.id || "",
documentType: document.documentType,
},
});
const { register, control, handleSubmit, reset } = useFormWithSchema(
documentUpdateSchema,
{
defaultValues: {
title: document.title,
ownerId: document.owner?.id || "",
documentType: document.documentType,
},
}
);
usePageTitle(document.title);
@@ -286,7 +302,9 @@ export default function DocumentDetailPage(props: Props) {
});
};
const handleUpdateDocumentType = (data: { documentType: typeof documentTypes[number] }) => {
const handleUpdateDocumentType = (data: {
documentType: (typeof documentTypes)[number];
}) => {
updateDocument({
variables: {
input: {
@@ -376,12 +394,17 @@ export default function DocumentDetailPage(props: Props) {
);
};
const handleDownloadPdf = (options: { withWatermark: boolean; withSignatures: boolean; watermarkEmail?: string }) => {
const handleDownloadPdf = (options: {
withWatermark: boolean;
withSignatures: boolean;
watermarkEmail?: string;
}) => {
const input = {
documentVersionId: currentVersion.id,
withWatermark: options.withWatermark,
withSignatures: options.withSignatures,
...(options.withWatermark && options.watermarkEmail && { watermarkEmail: options.watermarkEmail }),
...(options.withWatermark &&
options.watermarkEmail && { watermarkEmail: options.watermarkEmail }),
};
exportDocumentVersionPDF({
@@ -671,11 +694,7 @@ function EditablePropertyContent({
onClick={onSave}
disabled={disabled}
/>
<Button
variant="quaternary"
icon={IconCrossLargeX}
onClick={onCancel}
/>
<Button variant="quaternary" icon={IconCrossLargeX} onClick={onCancel} />
</div>
);
}
@@ -690,11 +709,7 @@ function ReadOnlyPropertyContent({
return (
<div className="flex items-center justify-between gap-3">
{children}
<Button
variant="quaternary"
icon={IconPencil}
onClick={onEdit}
/>
<Button variant="quaternary" icon={IconPencil} onClick={onEdit} />
</div>
);
}

View File

@@ -47,7 +47,10 @@ import type { DocumentsPageRowFragment$key } from "./__generated__/DocumentsPage
import { SortableTable, SortableTh } from "/components/SortableTable";
import { PublishDocumentsDialog } from "./dialogs/PublishDocumentsDialog.tsx";
import { SignatureDocumentsDialog } from "./dialogs/SignatureDocumentsDialog.tsx";
import { BulkExportDialog, type BulkExportDialogRef } from "/components/documents/BulkExportDialog";
import {
BulkExportDialog,
type BulkExportDialogRef,
} from "/components/documents/BulkExportDialog";
import type { DocumentsPageUserEmailQuery } from "./__generated__/DocumentsPageUserEmailQuery.graphql";
const documentsFragment = graphql`
@@ -103,7 +106,10 @@ export default function DocumentsPage(props: Props) {
props.queryRef
).organization;
const userEmailData = useLazyLoadQuery<DocumentsPageUserEmailQuery>(UserEmailQuery, {});
const userEmailData = useLazyLoadQuery<DocumentsPageUserEmailQuery>(
UserEmailQuery,
{}
);
const defaultEmail = userEmailData.viewer.user.email;
const pagination = usePaginationFragment(
documentsFragment,
@@ -116,7 +122,8 @@ export default function DocumentsPage(props: Props) {
const connectionId = pagination.data.documents.__id;
const [sendSigningNotifications] = useSendSigningNotificationsMutation();
const [bulkDeleteDocuments] = useBulkDeleteDocumentsMutation();
const [bulkExportDocuments, isBulkExporting] = useBulkExportDocumentsMutation();
const [bulkExportDocuments, isBulkExporting] =
useBulkExportDocumentsMutation();
const { list: selection, toggle, clear, reset } = useList<string>([]);
const confirm = useConfirm();
const bulkExportDialogRef = useRef<BulkExportDialogRef>(null);
@@ -145,21 +152,26 @@ export default function DocumentsPage(props: Props) {
{
message: sprintf(
__(
'This will permanently delete %s document%s. This action cannot be undone.'
"This will permanently delete %s document%s. This action cannot be undone."
),
documentCount,
documentCount > 1 ? 's' : ''
documentCount > 1 ? "s" : ""
),
}
);
};
const handleBulkExport = (options: { withWatermark: boolean; withSignatures: boolean; watermarkEmail?: string }) => {
const handleBulkExport = (options: {
withWatermark: boolean;
withSignatures: boolean;
watermarkEmail?: string;
}) => {
const input = {
documentIds: selection,
withWatermark: options.withWatermark,
withSignatures: options.withSignatures,
...(options.withWatermark && options.watermarkEmail && { watermarkEmail: options.watermarkEmail }),
...(options.withWatermark &&
options.watermarkEmail && { watermarkEmail: options.watermarkEmail }),
};
bulkExportDocuments({
@@ -196,14 +208,21 @@ export default function DocumentsPage(props: Props) {
<Tr>
<Th className="w-18">
<Checkbox
checked={selection.length === documents.length && documents.length > 0}
checked={
selection.length === documents.length &&
documents.length > 0
}
onChange={() => reset(documents.map((d) => d.id))}
/>
</Th>
<SortableTh field="TITLE" className="min-w-0">{__("Name")}</SortableTh>
<SortableTh field="TITLE" className="min-w-0">
{__("Name")}
</SortableTh>
<Th className="w-24">{__("Status")}</Th>
<Th className="w-20">{__("Version")}</Th>
<SortableTh field="DOCUMENT_TYPE" className="w-28">{__("Type")}</SortableTh>
<SortableTh field="DOCUMENT_TYPE" className="w-28">
{__("Type")}
</SortableTh>
<Th className="w-60">{__("Owner")}</Th>
<Th className="w-60">{__("Last update")}</Th>
<Th className="w-20">{__("Signatures")}</Th>
@@ -228,41 +247,48 @@ export default function DocumentsPage(props: Props) {
documentIds={selection}
onSave={clear}
>
<Button icon={IconCheckmark1} className="py-0.5 px-2 text-xs h-6 min-h-6">
<Button
icon={IconCheckmark1}
className="py-0.5 px-2 text-xs h-6 min-h-6"
>
{__("Publish")}
</Button>
</PublishDocumentsDialog>
<SignatureDocumentsDialog
documentIds={selection}
onSave={clear}
>
<Button variant="secondary" icon={IconSignature} className="py-0.5 px-2 text-xs h-6 min-h-6">
{__("Request signature")}
</Button>
</SignatureDocumentsDialog>
<BulkExportDialog
ref={bulkExportDialogRef}
onExport={handleBulkExport}
isLoading={isBulkExporting}
defaultEmail={defaultEmail}
selectedCount={selection.length}
>
<SignatureDocumentsDialog
documentIds={selection}
onSave={clear}
>
<Button
variant="secondary"
icon={IconSignature}
className="py-0.5 px-2 text-xs h-6 min-h-6"
>
{__("Request signature")}
</Button>
</SignatureDocumentsDialog>
<BulkExportDialog
ref={bulkExportDialogRef}
onExport={handleBulkExport}
isLoading={isBulkExporting}
defaultEmail={defaultEmail}
selectedCount={selection.length}
>
<Button
variant="secondary"
icon={IconArrowDown}
className="py-0.5 px-2 text-xs h-6 min-h-6"
>
{__("Export")}
</Button>
</BulkExportDialog>
<Button
variant="secondary"
icon={IconArrowDown}
variant="danger"
icon={IconTrashCan}
onClick={handleBulkDelete}
className="py-0.5 px-2 text-xs h-6 min-h-6"
>
{__("Export")}
{__("Delete")}
</Button>
</BulkExportDialog>
<Button
variant="danger"
icon={IconTrashCan}
onClick={handleBulkDelete}
className="py-0.5 px-2 text-xs h-6 min-h-6"
>
{__("Delete")}
</Button>
</div>
</div>
</Th>
@@ -315,7 +341,7 @@ const rowFragment = graphql`
id
status
version
signatures(first: 100) {
signatures(first: 500) {
edges {
node {
id
@@ -353,7 +379,9 @@ function DocumentRow({
const isDraft = lastVersion.status === "DRAFT";
const { __ } = useTranslate();
const signatures = lastVersion.signatures?.edges?.map((edge) => edge?.node)?.filter(Boolean) ?? [];
const signatures =
lastVersion.signatures?.edges?.map((edge) => edge?.node)?.filter(Boolean) ??
[];
const signedCount = signatures.filter(
(signature) => signature.state === "SIGNED"
).length;
@@ -385,9 +413,7 @@ function DocumentRow({
<Checkbox checked={checked} onChange={onCheck} />
</Td>
<Td className="min-w-0">
<div className="flex gap-4 items-center">
{document.title}
</div>
<div className="flex gap-4 items-center">{document.title}</div>
</Td>
<Td className="w-24">
<Badge variant={isDraft ? "neutral" : "success"}>
@@ -395,16 +421,16 @@ function DocumentRow({
</Badge>
</Td>
<Td className="w-20">v{lastVersion.version}</Td>
<Td className="w-28">{getDocumentTypeLabel(__, document.documentType)}</Td>
<Td className="w-28">
{getDocumentTypeLabel(__, document.documentType)}
</Td>
<Td className="w-60">
<div className="flex gap-2 items-center">
<Avatar name={document.owner?.fullName ?? ""} />
{document.owner?.fullName}
</div>
</Td>
<Td className="w-60">
{formatDate(document.updatedAt)}
</Td>
<Td className="w-60">{formatDate(document.updatedAt)}</Td>
<Td className="w-20">
{signedCount}/{signatures.length}
</Td>

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<993de5bc8086378a2993bf0d0cf13658>>
* @generated SignedSource<<1b07d7b3b18267f31c2b7ec7950edcb6>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -344,6 +344,6 @@ return {
};
})();
(node as any).hash = "1d06e505efa74bb0b564fcbfc57ce8c9";
(node as any).hash = "ffd5764324ccdcfe201e937980e75253";
export default node;

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<0b738ecabb86e683a0bae1f31700c0c4>>
* @generated SignedSource<<fc23e650e605a834fa70eab4d2a62cd2>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -313,7 +313,7 @@ return {
{
"kind": "Literal",
"name": "first",
"value": 100
"value": 500
}
],
"concreteType": "DocumentVersionSignatureConnection",
@@ -352,7 +352,7 @@ return {
"storageKey": null
}
],
"storageKey": "signatures(first:100)"
"storageKey": "signatures(first:500)"
}
],
"storageKey": null
@@ -452,12 +452,12 @@ return {
]
},
"params": {
"cacheID": "8cacafcbdbcd2d4f454d43a609aad010",
"cacheID": "32e25ba27cb9ad12640c0e338c16a804",
"id": null,
"metadata": {},
"name": "DocumentsListQuery",
"operationKind": "query",
"text": "query DocumentsListQuery(\n $after: CursorKey = null\n $before: CursorKey = null\n $first: Int = 50\n $last: Int = null\n $order: DocumentOrder = {field: TITLE, direction: ASC}\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...DocumentsPageListFragment_16fISc\n id\n }\n}\n\nfragment DocumentsPageListFragment_16fISc on Organization {\n documents(first: $first, after: $after, last: $last, before: $before, orderBy: $order) {\n edges {\n node {\n id\n ...DocumentsPageRowFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n id\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 version\n signatures(first: 100) {\n edges {\n node {\n id\n state\n }\n }\n }\n }\n }\n }\n}\n"
"text": "query DocumentsListQuery(\n $after: CursorKey = null\n $before: CursorKey = null\n $first: Int = 50\n $last: Int = null\n $order: DocumentOrder = {field: TITLE, direction: ASC}\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...DocumentsPageListFragment_16fISc\n id\n }\n}\n\nfragment DocumentsPageListFragment_16fISc on Organization {\n documents(first: $first, after: $after, last: $last, before: $before, orderBy: $order) {\n edges {\n node {\n id\n ...DocumentsPageRowFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n id\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 version\n signatures(first: 500) {\n edges {\n node {\n id\n state\n }\n }\n }\n }\n }\n }\n}\n"
}
};
})();

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<32d1bb469d4ece0eb14b746587eb45a1>>
* @generated SignedSource<<fdb5315d1a51b72e6f80370036f0f89d>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -160,7 +160,7 @@ return {
{
"kind": "Literal",
"name": "first",
"value": 100
"value": 500
}
],
"concreteType": "DocumentVersionSignatureConnection",
@@ -199,7 +199,7 @@ return {
"storageKey": null
}
],
"storageKey": "signatures(first:100)"
"storageKey": "signatures(first:500)"
}
],
"storageKey": null
@@ -216,6 +216,6 @@ return {
};
})();
(node as any).hash = "3cb482445055e1fea0e36ff3e7255cb5";
(node as any).hash = "66c9a0719081e20b4c796638b2f9976f";
export default node;

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<4d1e3c4d8c567a0b463ea1a1b7cc02f5>>
* @generated SignedSource<<d3b46cd525209def686ba33f7d6ff9bd>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -249,7 +249,7 @@ return {
{
"kind": "Literal",
"name": "first",
"value": 100
"value": 500
}
],
"concreteType": "DocumentVersionSignatureConnection",
@@ -288,7 +288,7 @@ return {
"storageKey": null
}
],
"storageKey": "signatures(first:100)"
"storageKey": "signatures(first:500)"
}
],
"storageKey": null
@@ -327,12 +327,12 @@ return {
]
},
"params": {
"cacheID": "271b1222b172816daeac0beb08737084",
"cacheID": "b43db972b1b28672faa60267ad642e43",
"id": null,
"metadata": {},
"name": "CreateDocumentDialogMutation",
"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 documentType\n updatedAt\n owner {\n id\n fullName\n }\n versions(first: 1) {\n edges {\n node {\n id\n status\n version\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 version\n signatures(first: 500) {\n edges {\n node {\n id\n state\n }\n }\n }\n }\n }\n }\n}\n"
}
};
})();

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<63939336fc37ddb7ced322612302c90b>>
* @generated SignedSource<<e35bdd76924adcc49210173df6dabdc3>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -233,7 +233,7 @@ return {
{
"kind": "Literal",
"name": "first",
"value": 100
"value": 500
}
],
"concreteType": "DocumentVersionSignatureConnection",
@@ -272,7 +272,7 @@ return {
"storageKey": null
}
],
"storageKey": "signatures(first:100)"
"storageKey": "signatures(first:500)"
}
],
"storageKey": null
@@ -295,12 +295,12 @@ return {
]
},
"params": {
"cacheID": "a5266ffa85ab7455c700dfe75ae74956",
"cacheID": "2ea3e8b6baf3eeb5b5b38f3e21b5998f",
"id": null,
"metadata": {},
"name": "PublishDocumentsDialogMutation",
"operationKind": "mutation",
"text": "mutation PublishDocumentsDialogMutation(\n $input: BulkPublishDocumentVersionsInput!\n) {\n bulkPublishDocumentVersions(input: $input) {\n documentEdges {\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 version\n signatures(first: 100) {\n edges {\n node {\n id\n state\n }\n }\n }\n }\n }\n }\n}\n"
"text": "mutation PublishDocumentsDialogMutation(\n $input: BulkPublishDocumentVersionsInput!\n) {\n bulkPublishDocumentVersions(input: $input) {\n documentEdges {\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 version\n signatures(first: 500) {\n edges {\n node {\n id\n state\n }\n }\n }\n }\n }\n }\n}\n"
}
};
})();