Add request signature dialog
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
committed by
Sacha Al Himdani
parent
8fd8319ad4
commit
9f03bc1873
@@ -56,7 +56,7 @@ export const paginatedPeopleQuery = graphql`
|
||||
}
|
||||
`;
|
||||
|
||||
const paginatedPeopleFragment = graphql`
|
||||
export const paginatedPeopleFragment = graphql`
|
||||
fragment PeopleGraphPaginatedFragment on Organization
|
||||
@refetchable(queryName: "PeopleListQuery")
|
||||
@argumentDefinitions(
|
||||
|
||||
@@ -39,9 +39,9 @@ import { sprintf, getDocumentTypeLabel } from "@probo/helpers";
|
||||
import { CreateDocumentDialog } from "./dialogs/CreateDocumentDialog";
|
||||
import type { DocumentsPageRowFragment$key } from "./__generated__/DocumentsPageRowFragment.graphql";
|
||||
import { SortableTable, SortableTh } from "/components/SortableTable";
|
||||
import { useMutationWithToasts } from "/hooks/useMutationWithToasts.ts";
|
||||
import { usePeople } from "/hooks/graph/PeopleGraph.ts";
|
||||
import { PublishDocumentsDialog } from "./dialogs/PublishDocumentsDialog.tsx";
|
||||
import { SignatureDocumentsDialog } from "./dialogs/SignatureDocumentsDialog.tsx";
|
||||
|
||||
const documentsFragment = graphql`
|
||||
fragment DocumentsPageListFragment on Organization
|
||||
@@ -74,19 +74,6 @@ const documentsFragment = graphql`
|
||||
}
|
||||
`;
|
||||
|
||||
const documentsSignatureMutation = graphql`
|
||||
mutation DocumentsPageSignatureMutation($input: BulkRequestSignaturesInput!) {
|
||||
bulkRequestSignatures(input: $input) {
|
||||
documentVersionSignatureEdges {
|
||||
node {
|
||||
id
|
||||
state
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
type Props = {
|
||||
queryRef: PreloadedQuery<DocumentGraphListQuery>;
|
||||
};
|
||||
@@ -108,13 +95,6 @@ export default function DocumentsPage(props: Props) {
|
||||
const [sendSigningNotifications] = useSendSigningNotificationsMutation();
|
||||
const { list: selection, toggle, clear, reset } = useList<string>([]);
|
||||
|
||||
const [signatureMutation, isSigning] = useMutationWithToasts(
|
||||
documentsSignatureMutation,
|
||||
{
|
||||
successMessage: __("Signature request send successfully"),
|
||||
errorMessage: __("Failed to send signature requests"),
|
||||
}
|
||||
);
|
||||
const people = usePeople(organization.id);
|
||||
usePageTitle(__("Documents"));
|
||||
|
||||
@@ -126,18 +106,6 @@ export default function DocumentsPage(props: Props) {
|
||||
});
|
||||
};
|
||||
|
||||
const requestSignatures = async () => {
|
||||
await signatureMutation({
|
||||
variables: {
|
||||
input: {
|
||||
documentIds: selection,
|
||||
signatoryIds: people.map((p) => p.id),
|
||||
},
|
||||
},
|
||||
});
|
||||
clear();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<PageHeader
|
||||
@@ -197,14 +165,14 @@ export default function DocumentsPage(props: Props) {
|
||||
>
|
||||
<Button icon={IconCheckmark1}>{__("Publish")}</Button>
|
||||
</PublishDocumentsDialog>
|
||||
<Button
|
||||
variant="secondary"
|
||||
icon={IconSignature}
|
||||
onClick={requestSignatures}
|
||||
disabled={isSigning}
|
||||
<SignatureDocumentsDialog
|
||||
documentIds={selection}
|
||||
onSave={clear}
|
||||
>
|
||||
{__("Request signature")}
|
||||
</Button>
|
||||
<Button variant="secondary" icon={IconSignature}>
|
||||
{__("Request signature")}
|
||||
</Button>
|
||||
</SignatureDocumentsDialog>
|
||||
</div>
|
||||
</div>
|
||||
</Th>
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
import {
|
||||
Button,
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogFooter,
|
||||
useDialogRef,
|
||||
Breadcrumb,
|
||||
Tr,
|
||||
Tbody,
|
||||
Td,
|
||||
Avatar,
|
||||
Spinner,
|
||||
Checkbox,
|
||||
Table,
|
||||
IconChevronDown,
|
||||
} from "@probo/ui";
|
||||
import { Suspense, type FormEventHandler, type ReactNode } from "react";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import { graphql } from "relay-runtime";
|
||||
import { sprintf } from "@probo/helpers";
|
||||
import { useMutationWithToasts } from "/hooks/useMutationWithToasts.ts";
|
||||
import { useOrganizationId } from "/hooks/useOrganizationId.ts";
|
||||
import {
|
||||
paginatedPeopleFragment,
|
||||
paginatedPeopleQuery,
|
||||
} from "/hooks/graph/PeopleGraph.ts";
|
||||
import { useList } from "@probo/hooks";
|
||||
import { useLazyLoadQuery, usePaginationFragment } from "react-relay";
|
||||
import type { PeopleGraphPaginatedFragment$key } from "/hooks/graph/__generated__/PeopleGraphPaginatedFragment.graphql.ts";
|
||||
import type { PeopleGraphPaginatedQuery } from "/hooks/graph/__generated__/PeopleGraphPaginatedQuery.graphql.ts";
|
||||
|
||||
type Props = {
|
||||
documentIds: string[];
|
||||
children: ReactNode;
|
||||
onSave: () => void;
|
||||
};
|
||||
|
||||
const documentsSignatureMutation = graphql`
|
||||
mutation SignatureDocumentsDialogMutation(
|
||||
$input: BulkRequestSignaturesInput!
|
||||
) {
|
||||
bulkRequestSignatures(input: $input) {
|
||||
documentVersionSignatureEdges {
|
||||
node {
|
||||
id
|
||||
state
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export function SignatureDocumentsDialog({
|
||||
documentIds,
|
||||
children,
|
||||
onSave,
|
||||
}: Props) {
|
||||
const { __ } = useTranslate();
|
||||
const dialogRef = useDialogRef();
|
||||
const { list: selectedPeople, toggle } = useList<string>([]);
|
||||
const [publishMutation] = useMutationWithToasts(documentsSignatureMutation, {
|
||||
successMessage: sprintf(
|
||||
__("%s signature requests sent"),
|
||||
documentIds.length
|
||||
),
|
||||
errorMessage: __("Failed to send signature requests"),
|
||||
});
|
||||
|
||||
const onSubmit: FormEventHandler<HTMLFormElement> = async (e) => {
|
||||
e.preventDefault();
|
||||
await publishMutation({
|
||||
variables: {
|
||||
input: {
|
||||
documentIds,
|
||||
signatoryIds: selectedPeople,
|
||||
},
|
||||
},
|
||||
});
|
||||
dialogRef.current?.close();
|
||||
onSave();
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
className="max-w-xl"
|
||||
ref={dialogRef}
|
||||
trigger={children}
|
||||
title={<Breadcrumb items={[__("Documents"), __("Signature requests")]} />}
|
||||
>
|
||||
<form onSubmit={onSubmit}>
|
||||
<DialogContent>
|
||||
<Suspense fallback={<Spinner />}>
|
||||
<PeopleList onChange={toggle} selectedPeople={selectedPeople} />
|
||||
</Suspense>
|
||||
</DialogContent>
|
||||
<DialogFooter>
|
||||
<Button type="submit" disabled={selectedPeople.length === 0}>
|
||||
{__("Send signature requests")}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
function PeopleList({
|
||||
onChange,
|
||||
selectedPeople,
|
||||
}: {
|
||||
onChange: (id: string) => void;
|
||||
selectedPeople: string[];
|
||||
}) {
|
||||
const { __ } = useTranslate();
|
||||
const organizationId = useOrganizationId();
|
||||
const data = useLazyLoadQuery<PeopleGraphPaginatedQuery>(
|
||||
paginatedPeopleQuery,
|
||||
{
|
||||
organizationId,
|
||||
}
|
||||
);
|
||||
const {
|
||||
data: page,
|
||||
hasNext,
|
||||
loadNext,
|
||||
isLoadingNext,
|
||||
} = usePaginationFragment(
|
||||
paginatedPeopleFragment,
|
||||
data.organization as PeopleGraphPaginatedFragment$key
|
||||
);
|
||||
const people = page.peoples.edges.map((edge) => edge.node);
|
||||
return (
|
||||
<>
|
||||
<Table className="border-none rounded-none">
|
||||
<Tbody>
|
||||
{people.map((person) => (
|
||||
<Tr key={person.id}>
|
||||
<Td width={75}>
|
||||
<Checkbox
|
||||
checked={selectedPeople.includes(person.id)}
|
||||
onChange={() => onChange(person.id)}
|
||||
/>
|
||||
</Td>
|
||||
<Td>
|
||||
<div className="flex gap-3 items-center">
|
||||
<Avatar name={person.fullName} />
|
||||
<div>
|
||||
<div className="text-sm">{person.fullName}</div>
|
||||
<div className="text-xs text-txt-tertiary">
|
||||
{person.primaryEmailAddress}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Td>
|
||||
</Tr>
|
||||
))}
|
||||
</Tbody>
|
||||
</Table>
|
||||
{isLoadingNext && <Spinner className="mt-3 mx-auto" />}
|
||||
{hasNext && (
|
||||
<Button
|
||||
variant="tertiary"
|
||||
onClick={() => loadNext(20)}
|
||||
className="mx-auto"
|
||||
icon={IconChevronDown}
|
||||
type="button"
|
||||
>
|
||||
{sprintf(__("Show %s more"), people.length)}
|
||||
</Button>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @generated SignedSource<<4c3514470041046f80f410ea5a3275d6>>
|
||||
* @generated SignedSource<<14890a4f10a8862a8aaf30ee1dc6f065>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
@@ -14,10 +14,10 @@ export type BulkRequestSignaturesInput = {
|
||||
documentIds: ReadonlyArray<string>;
|
||||
signatoryIds: ReadonlyArray<string>;
|
||||
};
|
||||
export type DocumentsPageSignatureMutation$variables = {
|
||||
export type SignatureDocumentsDialogMutation$variables = {
|
||||
input: BulkRequestSignaturesInput;
|
||||
};
|
||||
export type DocumentsPageSignatureMutation$data = {
|
||||
export type SignatureDocumentsDialogMutation$data = {
|
||||
readonly bulkRequestSignatures: {
|
||||
readonly documentVersionSignatureEdges: ReadonlyArray<{
|
||||
readonly node: {
|
||||
@@ -27,9 +27,9 @@ export type DocumentsPageSignatureMutation$data = {
|
||||
}>;
|
||||
};
|
||||
};
|
||||
export type DocumentsPageSignatureMutation = {
|
||||
response: DocumentsPageSignatureMutation$data;
|
||||
variables: DocumentsPageSignatureMutation$variables;
|
||||
export type SignatureDocumentsDialogMutation = {
|
||||
response: SignatureDocumentsDialogMutation$data;
|
||||
variables: SignatureDocumentsDialogMutation$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
@@ -100,7 +100,7 @@ return {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "DocumentsPageSignatureMutation",
|
||||
"name": "SignatureDocumentsDialogMutation",
|
||||
"selections": (v1/*: any*/),
|
||||
"type": "Mutation",
|
||||
"abstractKey": null
|
||||
@@ -109,20 +109,20 @@ return {
|
||||
"operation": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Operation",
|
||||
"name": "DocumentsPageSignatureMutation",
|
||||
"name": "SignatureDocumentsDialogMutation",
|
||||
"selections": (v1/*: any*/)
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "a61a828bc67d8b569c38f4826b42d00f",
|
||||
"cacheID": "4426d76c855d441caab5c932c2ceb2ba",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "DocumentsPageSignatureMutation",
|
||||
"name": "SignatureDocumentsDialogMutation",
|
||||
"operationKind": "mutation",
|
||||
"text": "mutation DocumentsPageSignatureMutation(\n $input: BulkRequestSignaturesInput!\n) {\n bulkRequestSignatures(input: $input) {\n documentVersionSignatureEdges {\n node {\n id\n state\n }\n }\n }\n}\n"
|
||||
"text": "mutation SignatureDocumentsDialogMutation(\n $input: BulkRequestSignaturesInput!\n) {\n bulkRequestSignatures(input: $input) {\n documentVersionSignatureEdges {\n node {\n id\n state\n }\n }\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "1cb2b7dfa938b35cf75b09b51cdd2dca";
|
||||
(node as any).hash = "1dbd9a7870416bf75d0a6fe44ebde408";
|
||||
|
||||
export default node;
|
||||
Reference in New Issue
Block a user