Add version selector on document
Fix #166 Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
committed by
Sacha Al Himdani
parent
a4606f95f5
commit
f114f3bc4a
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* @generated SignedSource<<5c3e7dba407e37cfc0203138d5db8318>>
|
* @generated SignedSource<<85c6bcb5e086b05009ccfaf8ac21a23e>>
|
||||||
* @lightSyntaxTransform
|
* @lightSyntaxTransform
|
||||||
* @nogrep
|
* @nogrep
|
||||||
*/
|
*/
|
||||||
@@ -387,6 +387,19 @@ return {
|
|||||||
"name": "updatedAt",
|
"name": "updatedAt",
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"concreteType": "People",
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "publishedBy",
|
||||||
|
"plural": false,
|
||||||
|
"selections": [
|
||||||
|
(v4/*: any*/),
|
||||||
|
(v3/*: any*/)
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"alias": null,
|
"alias": null,
|
||||||
"args": (v11/*: any*/),
|
"args": (v11/*: any*/),
|
||||||
@@ -475,26 +488,6 @@ return {
|
|||||||
"kind": "LinkedHandle",
|
"kind": "LinkedHandle",
|
||||||
"name": "signatures"
|
"name": "signatures"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"alias": null,
|
|
||||||
"args": null,
|
|
||||||
"kind": "ScalarField",
|
|
||||||
"name": "changelog",
|
|
||||||
"storageKey": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"alias": null,
|
|
||||||
"args": null,
|
|
||||||
"concreteType": "People",
|
|
||||||
"kind": "LinkedField",
|
|
||||||
"name": "publishedBy",
|
|
||||||
"plural": false,
|
|
||||||
"selections": [
|
|
||||||
(v4/*: any*/),
|
|
||||||
(v3/*: any*/)
|
|
||||||
],
|
|
||||||
"storageKey": null
|
|
||||||
},
|
|
||||||
(v2/*: any*/)
|
(v2/*: any*/)
|
||||||
],
|
],
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
@@ -527,12 +520,12 @@ return {
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"params": {
|
"params": {
|
||||||
"cacheID": "ad8c2b55a545807ab1fe72c21ed8f9c1",
|
"cacheID": "fbfe2cc393b8fc5349a92f3443f12e0e",
|
||||||
"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 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"
|
"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 publishedBy {\n fullName\n id\n }\n signatures(first: 100) {\n edges {\n node {\n id\n state\n signedBy {\n id\n }\n ...DocumentSignaturesTab_signature\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n}\n\nfragment DocumentSignaturesTab_signature on DocumentVersionSignature {\n id\n state\n signedAt\n requestedAt\n signedBy {\n fullName\n primaryEmailAddress\n id\n }\n}\n\nfragment LinkedControlsCardFragment on Control {\n id\n name\n sectionTitle\n framework {\n id\n name\n }\n}\n"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import type { PreloadedQuery } from "react-relay";
|
import type { PreloadedQuery } from "react-relay";
|
||||||
import type { DocumentGraphNodeQuery } from "/hooks/graph/__generated__/DocumentGraphNodeQuery.graphql";
|
|
||||||
import {
|
import {
|
||||||
ConnectionHandler,
|
ConnectionHandler,
|
||||||
graphql,
|
graphql,
|
||||||
@@ -7,42 +6,53 @@ import {
|
|||||||
useFragment,
|
useFragment,
|
||||||
usePreloadedQuery,
|
usePreloadedQuery,
|
||||||
} from "react-relay";
|
} from "react-relay";
|
||||||
|
import type { DocumentGraphNodeQuery } from "/hooks/graph/__generated__/DocumentGraphNodeQuery.graphql";
|
||||||
import {
|
import {
|
||||||
DocumentsConnectionKey,
|
|
||||||
documentNodeQuery,
|
documentNodeQuery,
|
||||||
|
DocumentsConnectionKey,
|
||||||
useDeleteDocumentMutation,
|
useDeleteDocumentMutation,
|
||||||
} from "/hooks/graph/DocumentGraph";
|
} from "/hooks/graph/DocumentGraph";
|
||||||
import { usePageTitle } from "@probo/hooks";
|
import { usePageTitle } from "@probo/hooks";
|
||||||
import type { DocumentDetailPageDocumentFragment$key } from "./__generated__/DocumentDetailPageDocumentFragment.graphql";
|
import type {
|
||||||
|
DocumentDetailPageDocumentFragment$data,
|
||||||
|
DocumentDetailPageDocumentFragment$key,
|
||||||
|
} from "./__generated__/DocumentDetailPageDocumentFragment.graphql";
|
||||||
import { useTranslate } from "@probo/i18n";
|
import { useTranslate } from "@probo/i18n";
|
||||||
import {
|
import {
|
||||||
PageHeader,
|
|
||||||
Breadcrumb,
|
|
||||||
IconCheckmark1,
|
|
||||||
PropertyRow,
|
|
||||||
Drawer,
|
|
||||||
Badge,
|
|
||||||
Avatar,
|
|
||||||
DropdownItem,
|
|
||||||
ActionDropdown,
|
ActionDropdown,
|
||||||
IconTrashCan,
|
Avatar,
|
||||||
IconPencil,
|
Badge,
|
||||||
|
Breadcrumb,
|
||||||
|
Button,
|
||||||
|
Drawer,
|
||||||
|
Dropdown,
|
||||||
|
DropdownItem,
|
||||||
|
IconCheckmark1,
|
||||||
|
IconChevronDown,
|
||||||
IconClock,
|
IconClock,
|
||||||
IconSignature,
|
IconPencil,
|
||||||
useConfirm,
|
IconTrashCan,
|
||||||
Tabs,
|
PageHeader,
|
||||||
TabLink,
|
PropertyRow,
|
||||||
TabBadge,
|
TabBadge,
|
||||||
|
TabLink,
|
||||||
|
Tabs,
|
||||||
|
useConfirm,
|
||||||
} from "@probo/ui";
|
} from "@probo/ui";
|
||||||
import { useOrganizationId } from "/hooks/useOrganizationId";
|
import { useOrganizationId } from "/hooks/useOrganizationId";
|
||||||
import { Button } from "@probo/ui";
|
|
||||||
import { useMutationWithToasts } from "/hooks/useMutationWithToasts";
|
import { useMutationWithToasts } from "/hooks/useMutationWithToasts";
|
||||||
import { sprintf, getDocumentTypeLabel } from "@probo/helpers";
|
import { getDocumentTypeLabel, sprintf } from "@probo/helpers";
|
||||||
import { Outlet, useNavigate } from "react-router";
|
import {
|
||||||
|
Link,
|
||||||
|
Outlet,
|
||||||
|
useLocation,
|
||||||
|
useNavigate,
|
||||||
|
useParams,
|
||||||
|
} from "react-router";
|
||||||
import UpdateVersionDialog from "./dialogs/UpdateVersionDialog";
|
import UpdateVersionDialog from "./dialogs/UpdateVersionDialog";
|
||||||
import { useRef } from "react";
|
import { useRef } from "react";
|
||||||
import { DocumentVersionHistoryDialog } from "./dialogs/DocumentVersionHistoryDialog";
|
import type { NodeOf } from "/types.ts";
|
||||||
import { DocumentSignaturesDialog } from "./dialogs/DocumentSignaturesDialog";
|
import clsx from "clsx";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
queryRef: PreloadedQuery<DocumentGraphNodeQuery>;
|
queryRef: PreloadedQuery<DocumentGraphNodeQuery>;
|
||||||
@@ -71,6 +81,9 @@ const documentFragment = graphql`
|
|||||||
publishedAt
|
publishedAt
|
||||||
version
|
version
|
||||||
updatedAt
|
updatedAt
|
||||||
|
publishedBy {
|
||||||
|
fullName
|
||||||
|
}
|
||||||
signatures(first: 100)
|
signatures(first: 100)
|
||||||
@connection(key: "DocumentDetailPage_signatures") {
|
@connection(key: "DocumentDetailPage_signatures") {
|
||||||
__id
|
__id
|
||||||
@@ -81,12 +94,10 @@ const documentFragment = graphql`
|
|||||||
signedBy {
|
signedBy {
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
...DocumentSignaturesDialog_signature
|
...DocumentSignaturesTab_signature
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
...DocumentVersionHistoryDialogFragment
|
|
||||||
...DocumentSignaturesDialog_version
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -106,22 +117,28 @@ const publishDocumentVersionMutation = graphql`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export default function DocumentDetailPage(props: Props) {
|
export default function DocumentDetailPage(props: Props) {
|
||||||
|
const { versionId } = useParams<{ versionId?: string }>();
|
||||||
const node = usePreloadedQuery(documentNodeQuery, props.queryRef).node;
|
const node = usePreloadedQuery(documentNodeQuery, props.queryRef).node;
|
||||||
const document = useFragment(
|
const document = useFragment(
|
||||||
documentFragment,
|
documentFragment,
|
||||||
node as DocumentDetailPageDocumentFragment$key
|
node as DocumentDetailPageDocumentFragment$key,
|
||||||
);
|
);
|
||||||
const { __, dateFormat } = useTranslate();
|
const { __, dateFormat } = useTranslate();
|
||||||
const organizationId = useOrganizationId();
|
const organizationId = useOrganizationId();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const lastVersion = document.versions.edges[0].node;
|
const versions = document.versions.edges.map((edge) => edge.node);
|
||||||
const isDraft = lastVersion.status === "DRAFT";
|
const currentVersion =
|
||||||
|
document.versions.edges.find((v) => v.node.id === versionId)?.node ??
|
||||||
|
document.versions.edges[0].node;
|
||||||
|
const signatures = currentVersion.signatures.edges.map((s) => s.node);
|
||||||
|
const signedSignatures = signatures.filter((s) => s.state === "SIGNED");
|
||||||
|
const isDraft = currentVersion.status === "DRAFT";
|
||||||
const [publishDocumentVersion, isPublishing] = useMutationWithToasts(
|
const [publishDocumentVersion, isPublishing] = useMutationWithToasts(
|
||||||
publishDocumentVersionMutation,
|
publishDocumentVersionMutation,
|
||||||
{
|
{
|
||||||
successMessage: __("Document published successfully."),
|
successMessage: __("Document published successfully."),
|
||||||
errorMessage: __("Failed to publish document. Please try again."),
|
errorMessage: __("Failed to publish document. Please try again."),
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
const [deleteDocument, isDeleting] = useDeleteDocumentMutation();
|
const [deleteDocument, isDeleting] = useDeleteDocumentMutation();
|
||||||
const versionConnectionId = document.versions.__id;
|
const versionConnectionId = document.versions.__id;
|
||||||
@@ -139,7 +156,7 @@ export default function DocumentDetailPage(props: Props) {
|
|||||||
props.queryRef.environment,
|
props.queryRef.environment,
|
||||||
documentNodeQuery,
|
documentNodeQuery,
|
||||||
props.queryRef.variables,
|
props.queryRef.variables,
|
||||||
{ fetchPolicy: "network-only" }
|
{ fetchPolicy: "network-only" },
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -153,7 +170,7 @@ export default function DocumentDetailPage(props: Props) {
|
|||||||
new Promise<void>((resolve) => {
|
new Promise<void>((resolve) => {
|
||||||
const connectionId = ConnectionHandler.getConnectionID(
|
const connectionId = ConnectionHandler.getConnectionID(
|
||||||
organizationId,
|
organizationId,
|
||||||
DocumentsConnectionKey
|
DocumentsConnectionKey,
|
||||||
);
|
);
|
||||||
deleteDocument({
|
deleteDocument({
|
||||||
variables: {
|
variables: {
|
||||||
@@ -169,16 +186,19 @@ export default function DocumentDetailPage(props: Props) {
|
|||||||
{
|
{
|
||||||
message: sprintf(
|
message: sprintf(
|
||||||
__(
|
__(
|
||||||
'This will permanently delete the document "%s". This action cannot be undone.'
|
'This will permanently delete the document "%s". This action cannot be undone.',
|
||||||
),
|
),
|
||||||
document.title
|
document.title,
|
||||||
),
|
),
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateDialogRef = useRef<{ open: () => void }>(null);
|
const updateDialogRef = useRef<{ open: () => void }>(null);
|
||||||
const controlsCount = document.controlsInfo.totalCount;
|
const controlsCount = document.controlsInfo.totalCount;
|
||||||
|
const urlPrefix = versionId
|
||||||
|
? `/organizations/${organizationId}/documents/${document.id}/versions/${versionId}`
|
||||||
|
: `/organizations/${organizationId}/documents/${document.id}`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -210,16 +230,155 @@ export default function DocumentDetailPage(props: Props) {
|
|||||||
{__("Publish")}
|
{__("Publish")}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
<DocumentVersionHistoryDialog document={document}>
|
<Dropdown
|
||||||
|
toggle={
|
||||||
<Button icon={IconClock} variant="secondary">
|
<Button icon={IconClock} variant="secondary">
|
||||||
{__("Version history")}
|
{__("Version history")}
|
||||||
|
<IconChevronDown size={12} />
|
||||||
</Button>
|
</Button>
|
||||||
</DocumentVersionHistoryDialog>
|
}
|
||||||
<DocumentSignaturesDialog document={document}>
|
>
|
||||||
<Button icon={IconSignature} variant="secondary">
|
{versions.map((version) => (
|
||||||
{__("Signatures history")}
|
<DropdownItem asChild key={version.id}>
|
||||||
</Button>
|
<VersionItem
|
||||||
</DocumentSignaturesDialog>
|
document={document}
|
||||||
|
version={version}
|
||||||
|
active={version.id === currentVersion.id}
|
||||||
|
onSelect={() => {}}
|
||||||
|
/>
|
||||||
|
</DropdownItem>
|
||||||
|
))}
|
||||||
|
{versions.map((version) => (
|
||||||
|
<DropdownItem asChild key={version.id}>
|
||||||
|
<VersionItem
|
||||||
|
document={document}
|
||||||
|
version={version}
|
||||||
|
active={version.id === currentVersion.id}
|
||||||
|
onSelect={() => {}}
|
||||||
|
/>
|
||||||
|
</DropdownItem>
|
||||||
|
))}
|
||||||
|
{versions.map((version) => (
|
||||||
|
<DropdownItem asChild key={version.id}>
|
||||||
|
<VersionItem
|
||||||
|
document={document}
|
||||||
|
version={version}
|
||||||
|
active={version.id === currentVersion.id}
|
||||||
|
onSelect={() => {}}
|
||||||
|
/>
|
||||||
|
</DropdownItem>
|
||||||
|
))}
|
||||||
|
{versions.map((version) => (
|
||||||
|
<DropdownItem asChild key={version.id}>
|
||||||
|
<VersionItem
|
||||||
|
document={document}
|
||||||
|
version={version}
|
||||||
|
active={version.id === currentVersion.id}
|
||||||
|
onSelect={() => {}}
|
||||||
|
/>
|
||||||
|
</DropdownItem>
|
||||||
|
))}
|
||||||
|
{versions.map((version) => (
|
||||||
|
<DropdownItem asChild key={version.id}>
|
||||||
|
<VersionItem
|
||||||
|
document={document}
|
||||||
|
version={version}
|
||||||
|
active={version.id === currentVersion.id}
|
||||||
|
onSelect={() => {}}
|
||||||
|
/>
|
||||||
|
</DropdownItem>
|
||||||
|
))}
|
||||||
|
{versions.map((version) => (
|
||||||
|
<DropdownItem asChild key={version.id}>
|
||||||
|
<VersionItem
|
||||||
|
document={document}
|
||||||
|
version={version}
|
||||||
|
active={version.id === currentVersion.id}
|
||||||
|
onSelect={() => {}}
|
||||||
|
/>
|
||||||
|
</DropdownItem>
|
||||||
|
))}
|
||||||
|
{versions.map((version) => (
|
||||||
|
<DropdownItem asChild key={version.id}>
|
||||||
|
<VersionItem
|
||||||
|
document={document}
|
||||||
|
version={version}
|
||||||
|
active={version.id === currentVersion.id}
|
||||||
|
onSelect={() => {}}
|
||||||
|
/>
|
||||||
|
</DropdownItem>
|
||||||
|
))}
|
||||||
|
{versions.map((version) => (
|
||||||
|
<DropdownItem asChild key={version.id}>
|
||||||
|
<VersionItem
|
||||||
|
document={document}
|
||||||
|
version={version}
|
||||||
|
active={version.id === currentVersion.id}
|
||||||
|
onSelect={() => {}}
|
||||||
|
/>
|
||||||
|
</DropdownItem>
|
||||||
|
))}
|
||||||
|
{versions.map((version) => (
|
||||||
|
<DropdownItem asChild key={version.id}>
|
||||||
|
<VersionItem
|
||||||
|
document={document}
|
||||||
|
version={version}
|
||||||
|
active={version.id === currentVersion.id}
|
||||||
|
onSelect={() => {}}
|
||||||
|
/>
|
||||||
|
</DropdownItem>
|
||||||
|
))}
|
||||||
|
{versions.map((version) => (
|
||||||
|
<DropdownItem asChild key={version.id}>
|
||||||
|
<VersionItem
|
||||||
|
document={document}
|
||||||
|
version={version}
|
||||||
|
active={version.id === currentVersion.id}
|
||||||
|
onSelect={() => {}}
|
||||||
|
/>
|
||||||
|
</DropdownItem>
|
||||||
|
))}
|
||||||
|
{versions.map((version) => (
|
||||||
|
<DropdownItem asChild key={version.id}>
|
||||||
|
<VersionItem
|
||||||
|
document={document}
|
||||||
|
version={version}
|
||||||
|
active={version.id === currentVersion.id}
|
||||||
|
onSelect={() => {}}
|
||||||
|
/>
|
||||||
|
</DropdownItem>
|
||||||
|
))}
|
||||||
|
{versions.map((version) => (
|
||||||
|
<DropdownItem asChild key={version.id}>
|
||||||
|
<VersionItem
|
||||||
|
document={document}
|
||||||
|
version={version}
|
||||||
|
active={version.id === currentVersion.id}
|
||||||
|
onSelect={() => {}}
|
||||||
|
/>
|
||||||
|
</DropdownItem>
|
||||||
|
))}
|
||||||
|
{versions.map((version) => (
|
||||||
|
<DropdownItem asChild key={version.id}>
|
||||||
|
<VersionItem
|
||||||
|
document={document}
|
||||||
|
version={version}
|
||||||
|
active={version.id === currentVersion.id}
|
||||||
|
onSelect={() => {}}
|
||||||
|
/>
|
||||||
|
</DropdownItem>
|
||||||
|
))}
|
||||||
|
{versions.map((version) => (
|
||||||
|
<DropdownItem asChild key={version.id}>
|
||||||
|
<VersionItem
|
||||||
|
document={document}
|
||||||
|
version={version}
|
||||||
|
active={version.id === currentVersion.id}
|
||||||
|
onSelect={() => {}}
|
||||||
|
/>
|
||||||
|
</DropdownItem>
|
||||||
|
))}
|
||||||
|
</Dropdown>
|
||||||
|
|
||||||
<ActionDropdown variant="secondary">
|
<ActionDropdown variant="secondary">
|
||||||
<DropdownItem
|
<DropdownItem
|
||||||
@@ -242,20 +401,20 @@ export default function DocumentDetailPage(props: Props) {
|
|||||||
<PageHeader title={document.title} />
|
<PageHeader title={document.title} />
|
||||||
|
|
||||||
<Tabs>
|
<Tabs>
|
||||||
<TabLink
|
<TabLink to={`${urlPrefix}/description`}>{__("Description")}</TabLink>
|
||||||
to={`/organizations/${organizationId}/documents/${document.id}/description`}
|
<TabLink to={`${urlPrefix}/controls`}>
|
||||||
>
|
|
||||||
{__("Description")}
|
|
||||||
</TabLink>
|
|
||||||
<TabLink
|
|
||||||
to={`/organizations/${organizationId}/documents/${document.id}/controls`}
|
|
||||||
>
|
|
||||||
{__("Controls")}
|
{__("Controls")}
|
||||||
<TabBadge>{controlsCount}</TabBadge>
|
<TabBadge>{controlsCount}</TabBadge>
|
||||||
</TabLink>
|
</TabLink>
|
||||||
|
<TabLink to={`${urlPrefix}/signatures`}>
|
||||||
|
{__("Signatures")}
|
||||||
|
<TabBadge>
|
||||||
|
{signedSignatures.length}/{signatures.length}
|
||||||
|
</TabBadge>
|
||||||
|
</TabLink>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
||||||
<Outlet context={{ document, lastVersion }} />
|
<Outlet context={{ document, version: currentVersion }} />
|
||||||
</div>
|
</div>
|
||||||
<Drawer>
|
<Drawer>
|
||||||
<div className="text-base text-txt-primary font-medium mb-4">
|
<div className="text-base text-txt-primary font-medium mb-4">
|
||||||
@@ -283,18 +442,18 @@ export default function DocumentDetailPage(props: Props) {
|
|||||||
</PropertyRow>
|
</PropertyRow>
|
||||||
<PropertyRow label={__("Version")}>
|
<PropertyRow label={__("Version")}>
|
||||||
<div className="text-sm text-txt-secondary">
|
<div className="text-sm text-txt-secondary">
|
||||||
{lastVersion.version}
|
{currentVersion.version}
|
||||||
</div>
|
</div>
|
||||||
</PropertyRow>
|
</PropertyRow>
|
||||||
<PropertyRow label={__("Last modified")}>
|
<PropertyRow label={__("Last modified")}>
|
||||||
<div className="text-sm text-txt-secondary">
|
<div className="text-sm text-txt-secondary">
|
||||||
{dateFormat(lastVersion.updatedAt)}
|
{dateFormat(currentVersion.updatedAt)}
|
||||||
</div>
|
</div>
|
||||||
</PropertyRow>
|
</PropertyRow>
|
||||||
{lastVersion.publishedAt && (
|
{currentVersion.publishedAt && (
|
||||||
<PropertyRow label={__("Published Date")}>
|
<PropertyRow label={__("Published Date")}>
|
||||||
<div className="text-sm text-txt-secondary">
|
<div className="text-sm text-txt-secondary">
|
||||||
{dateFormat(lastVersion.publishedAt)}
|
{dateFormat(currentVersion.publishedAt)}
|
||||||
</div>
|
</div>
|
||||||
</PropertyRow>
|
</PropertyRow>
|
||||||
)}
|
)}
|
||||||
@@ -302,3 +461,46 @@ export default function DocumentDetailPage(props: Props) {
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Version = NodeOf<DocumentDetailPageDocumentFragment$data["versions"]>;
|
||||||
|
|
||||||
|
function VersionItem({
|
||||||
|
document,
|
||||||
|
version,
|
||||||
|
active,
|
||||||
|
onSelect,
|
||||||
|
...props
|
||||||
|
}: {
|
||||||
|
document: DocumentDetailPageDocumentFragment$data;
|
||||||
|
version: Version;
|
||||||
|
active?: boolean;
|
||||||
|
onSelect: (v: Version) => void;
|
||||||
|
}) {
|
||||||
|
const { dateTimeFormat } = useTranslate();
|
||||||
|
const organizationId = useOrganizationId();
|
||||||
|
const suffix = useLocation().pathname.split("/").at(-1);
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
to={`/organizations/${organizationId}/documents/${document.id}/versions/${version.id}/${suffix}`}
|
||||||
|
onClick={() => onSelect(version)}
|
||||||
|
className={clsx(
|
||||||
|
"flex items-center gap-2 py-2 px-[10px] w-full hover:bg-tertiary-hover cursor-pointer rounded",
|
||||||
|
active && "bg-tertiary-pressed",
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<Avatar
|
||||||
|
name={version.publishedBy?.fullName ?? document.owner.fullName}
|
||||||
|
size="l"
|
||||||
|
/>
|
||||||
|
<div className="text-start space-y-[2px] w-full overflow-hidden">
|
||||||
|
<div className="text-sm text-txt-primary whitespace-nowrap overflow-hidden text-ellipsis">
|
||||||
|
{version.publishedBy?.fullName ?? document.owner.fullName}
|
||||||
|
</div>
|
||||||
|
<div className="text-xs text-txt-secondary whitespace-nowrap overflow-hidden text-ellipsis">
|
||||||
|
{dateTimeFormat(version.publishedAt ?? version.updatedAt)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* @generated SignedSource<<4318aaecefe001fe6f42e6db26c421b4>>
|
* @generated SignedSource<<8db0c7dcd7ff31c01ec5facd5a3b598a>>
|
||||||
* @lightSyntaxTransform
|
* @lightSyntaxTransform
|
||||||
* @nogrep
|
* @nogrep
|
||||||
*/
|
*/
|
||||||
@@ -31,6 +31,9 @@ export type DocumentDetailPageDocumentFragment$data = {
|
|||||||
readonly content: string;
|
readonly content: string;
|
||||||
readonly id: string;
|
readonly id: string;
|
||||||
readonly publishedAt: any | null | undefined;
|
readonly publishedAt: any | null | undefined;
|
||||||
|
readonly publishedBy: {
|
||||||
|
readonly fullName: string;
|
||||||
|
} | null | undefined;
|
||||||
readonly signatures: {
|
readonly signatures: {
|
||||||
readonly __id: string;
|
readonly __id: string;
|
||||||
readonly edges: ReadonlyArray<{
|
readonly edges: ReadonlyArray<{
|
||||||
@@ -40,14 +43,13 @@ export type DocumentDetailPageDocumentFragment$data = {
|
|||||||
readonly id: string;
|
readonly id: string;
|
||||||
};
|
};
|
||||||
readonly state: DocumentVersionSignatureState;
|
readonly state: DocumentVersionSignatureState;
|
||||||
readonly " $fragmentSpreads": FragmentRefs<"DocumentSignaturesDialog_signature">;
|
readonly " $fragmentSpreads": FragmentRefs<"DocumentSignaturesTab_signature">;
|
||||||
};
|
};
|
||||||
}>;
|
}>;
|
||||||
};
|
};
|
||||||
readonly status: DocumentStatus;
|
readonly status: DocumentStatus;
|
||||||
readonly updatedAt: any;
|
readonly updatedAt: any;
|
||||||
readonly version: number;
|
readonly version: number;
|
||||||
readonly " $fragmentSpreads": FragmentRefs<"DocumentSignaturesDialog_version" | "DocumentVersionHistoryDialogFragment">;
|
|
||||||
};
|
};
|
||||||
}>;
|
}>;
|
||||||
};
|
};
|
||||||
@@ -71,17 +73,24 @@ v1 = {
|
|||||||
"alias": null,
|
"alias": null,
|
||||||
"args": null,
|
"args": null,
|
||||||
"kind": "ScalarField",
|
"kind": "ScalarField",
|
||||||
"name": "__typename",
|
"name": "fullName",
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
},
|
},
|
||||||
v2 = {
|
v2 = {
|
||||||
"alias": null,
|
"alias": null,
|
||||||
"args": null,
|
"args": null,
|
||||||
"kind": "ScalarField",
|
"kind": "ScalarField",
|
||||||
"name": "cursor",
|
"name": "__typename",
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
},
|
},
|
||||||
v3 = {
|
v3 = {
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "cursor",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
v4 = {
|
||||||
"alias": null,
|
"alias": null,
|
||||||
"args": null,
|
"args": null,
|
||||||
"concreteType": "PageInfo",
|
"concreteType": "PageInfo",
|
||||||
@@ -106,7 +115,7 @@ v3 = {
|
|||||||
],
|
],
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
},
|
},
|
||||||
v4 = {
|
v5 = {
|
||||||
"kind": "ClientExtension",
|
"kind": "ClientExtension",
|
||||||
"selections": [
|
"selections": [
|
||||||
{
|
{
|
||||||
@@ -165,13 +174,7 @@ return {
|
|||||||
"plural": false,
|
"plural": false,
|
||||||
"selections": [
|
"selections": [
|
||||||
(v0/*: any*/),
|
(v0/*: any*/),
|
||||||
{
|
(v1/*: any*/)
|
||||||
"alias": null,
|
|
||||||
"args": null,
|
|
||||||
"kind": "ScalarField",
|
|
||||||
"name": "fullName",
|
|
||||||
"storageKey": null
|
|
||||||
}
|
|
||||||
],
|
],
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
},
|
},
|
||||||
@@ -264,6 +267,18 @@ return {
|
|||||||
"name": "updatedAt",
|
"name": "updatedAt",
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"concreteType": "People",
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "publishedBy",
|
||||||
|
"plural": false,
|
||||||
|
"selections": [
|
||||||
|
(v1/*: any*/)
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"alias": "signatures",
|
"alias": "signatures",
|
||||||
"args": null,
|
"args": null,
|
||||||
@@ -311,9 +326,18 @@ return {
|
|||||||
{
|
{
|
||||||
"args": null,
|
"args": null,
|
||||||
"kind": "FragmentSpread",
|
"kind": "FragmentSpread",
|
||||||
"name": "DocumentSignaturesDialog_signature"
|
"name": "DocumentSignaturesTab_signature"
|
||||||
},
|
},
|
||||||
(v1/*: any*/)
|
(v2/*: any*/)
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
(v3/*: any*/)
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
(v4/*: any*/),
|
||||||
|
(v5/*: any*/)
|
||||||
],
|
],
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
},
|
},
|
||||||
@@ -321,31 +345,12 @@ return {
|
|||||||
],
|
],
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
},
|
},
|
||||||
(v3/*: any*/),
|
(v3/*: any*/)
|
||||||
(v4/*: any*/)
|
|
||||||
],
|
],
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
},
|
},
|
||||||
{
|
(v4/*: any*/),
|
||||||
"args": null,
|
(v5/*: any*/)
|
||||||
"kind": "FragmentSpread",
|
|
||||||
"name": "DocumentVersionHistoryDialogFragment"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"args": null,
|
|
||||||
"kind": "FragmentSpread",
|
|
||||||
"name": "DocumentSignaturesDialog_version"
|
|
||||||
},
|
|
||||||
(v1/*: any*/)
|
|
||||||
],
|
|
||||||
"storageKey": null
|
|
||||||
},
|
|
||||||
(v2/*: any*/)
|
|
||||||
],
|
|
||||||
"storageKey": null
|
|
||||||
},
|
|
||||||
(v3/*: any*/),
|
|
||||||
(v4/*: any*/)
|
|
||||||
],
|
],
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
}
|
}
|
||||||
@@ -355,6 +360,6 @@ return {
|
|||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
(node as any).hash = "da1a4055d701fa9c3e0e0e50df24860f";
|
(node as any).hash = "1523ddad229ae2d6bf94d30de5579ae6";
|
||||||
|
|
||||||
export default node;
|
export default node;
|
||||||
|
|||||||
@@ -1,167 +0,0 @@
|
|||||||
import {
|
|
||||||
Avatar,
|
|
||||||
Button,
|
|
||||||
Dialog,
|
|
||||||
DialogContent,
|
|
||||||
DialogFooter,
|
|
||||||
DialogTitle,
|
|
||||||
IconArrowInbox,
|
|
||||||
Markdown,
|
|
||||||
} from "@probo/ui";
|
|
||||||
import { graphql } from "relay-runtime";
|
|
||||||
import { useTranslate } from "@probo/i18n";
|
|
||||||
import { useState, type ReactNode } from "react";
|
|
||||||
import { useFragment } from "react-relay";
|
|
||||||
import type { DocumentVersionHistoryDialogFragment$key } from "./__generated__/DocumentVersionHistoryDialogFragment.graphql";
|
|
||||||
import type { DocumentVersionHistoryDialogExportPDFMutation } from "./__generated__/DocumentVersionHistoryDialogExportPDFMutation.graphql";
|
|
||||||
import clsx from "clsx";
|
|
||||||
import type { NodeOf } from "/types";
|
|
||||||
import type { DocumentDetailPageDocumentFragment$data } from "../__generated__/DocumentDetailPageDocumentFragment.graphql";
|
|
||||||
import { useMutationWithToasts } from "/hooks/useMutationWithToasts";
|
|
||||||
|
|
||||||
const historyFragment = graphql`
|
|
||||||
fragment DocumentVersionHistoryDialogFragment on DocumentVersion {
|
|
||||||
id
|
|
||||||
version
|
|
||||||
status
|
|
||||||
content
|
|
||||||
changelog
|
|
||||||
publishedAt
|
|
||||||
updatedAt
|
|
||||||
publishedBy {
|
|
||||||
fullName
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const exportPDFMutation = graphql`
|
|
||||||
mutation DocumentVersionHistoryDialogExportPDFMutation(
|
|
||||||
$documentVersionId: ID!
|
|
||||||
) {
|
|
||||||
exportDocumentVersionPDF(input: { documentVersionId: $documentVersionId }) {
|
|
||||||
data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
document: DocumentDetailPageDocumentFragment$data;
|
|
||||||
children?: ReactNode;
|
|
||||||
};
|
|
||||||
|
|
||||||
type Version = NodeOf<DocumentDetailPageDocumentFragment$data["versions"]>;
|
|
||||||
|
|
||||||
export function DocumentVersionHistoryDialog(props: Props) {
|
|
||||||
const { __ } = useTranslate();
|
|
||||||
const versions = props.document.versions.edges.map((edge) => edge.node);
|
|
||||||
const [selectedVersion, setSelectedVersion] = useState<Version>(versions[0]);
|
|
||||||
const [exportPDF, isExporting] =
|
|
||||||
useMutationWithToasts<DocumentVersionHistoryDialogExportPDFMutation>(
|
|
||||||
exportPDFMutation,
|
|
||||||
{
|
|
||||||
errorMessage: __("Failed to export PDF"),
|
|
||||||
successMessage: __("PDF exported successfully"),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleDownloadPDF = () => {
|
|
||||||
exportPDF({
|
|
||||||
variables: { documentVersionId: selectedVersion.id },
|
|
||||||
onCompleted: (data) => {
|
|
||||||
if (data.exportDocumentVersionPDF.data) {
|
|
||||||
// Handle data URI format (data:application/pdf;base64,...)
|
|
||||||
const dataUri = data.exportDocumentVersionPDF.data;
|
|
||||||
|
|
||||||
// Extract base64 part from data URI
|
|
||||||
const base64Data = dataUri.split(",")[1];
|
|
||||||
|
|
||||||
// Convert base64 to blob and download
|
|
||||||
const binaryString = atob(base64Data);
|
|
||||||
const bytes = new Uint8Array(binaryString.length);
|
|
||||||
for (let i = 0; i < binaryString.length; i++) {
|
|
||||||
bytes[i] = binaryString.charCodeAt(i);
|
|
||||||
}
|
|
||||||
const blob = new Blob([bytes], { type: "application/pdf" });
|
|
||||||
const url = URL.createObjectURL(blob);
|
|
||||||
const link = document.createElement("a");
|
|
||||||
link.href = url;
|
|
||||||
link.download = `${props.document.title}-v${selectedVersion.version}.pdf`;
|
|
||||||
document.body.appendChild(link);
|
|
||||||
link.click();
|
|
||||||
document.body.removeChild(link);
|
|
||||||
URL.revokeObjectURL(url);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Dialog trigger={props.children}>
|
|
||||||
<DialogContent className="flex" scrollableChildren>
|
|
||||||
<aside className="p-6 overflow-y-auto w-60 flex-none space-y-2">
|
|
||||||
<DialogTitle className="text-base text-txt-primary font-medium mb-4">
|
|
||||||
{__("Version History")}
|
|
||||||
</DialogTitle>
|
|
||||||
{versions.map((version) => (
|
|
||||||
<VersionItem
|
|
||||||
key={version.id}
|
|
||||||
document={props.document}
|
|
||||||
version={version}
|
|
||||||
active={selectedVersion === version}
|
|
||||||
onSelect={setSelectedVersion}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</aside>
|
|
||||||
<main className="flex-1 px-12 py-8">
|
|
||||||
<div className="mb-4">
|
|
||||||
<Button
|
|
||||||
onClick={handleDownloadPDF}
|
|
||||||
icon={IconArrowInbox}
|
|
||||||
variant="secondary"
|
|
||||||
disabled={isExporting}
|
|
||||||
>
|
|
||||||
{isExporting ? __("Exporting...") : __("Download PDF")}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<Markdown content={selectedVersion.content} />
|
|
||||||
</main>
|
|
||||||
</DialogContent>
|
|
||||||
<DialogFooter />
|
|
||||||
</Dialog>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function VersionItem(props: {
|
|
||||||
document: DocumentDetailPageDocumentFragment$data;
|
|
||||||
version: Version;
|
|
||||||
active?: boolean;
|
|
||||||
onSelect: (v: Version) => void;
|
|
||||||
}) {
|
|
||||||
const version = useFragment<DocumentVersionHistoryDialogFragment$key>(
|
|
||||||
historyFragment,
|
|
||||||
props.version
|
|
||||||
);
|
|
||||||
const { dateTimeFormat } = useTranslate();
|
|
||||||
return (
|
|
||||||
<button
|
|
||||||
onClick={() => props.onSelect(props.version)}
|
|
||||||
className={clsx(
|
|
||||||
"flex items-center gap-2 py-2 px-[10px] w-full hover:bg-tertiary-hover cursor-pointer rounded",
|
|
||||||
props.active && "bg-tertiary-pressed"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<Avatar
|
|
||||||
name={version.publishedBy?.fullName ?? props.document.owner.fullName}
|
|
||||||
size="l"
|
|
||||||
/>
|
|
||||||
<div className="text-start space-y-[2px] w-full overflow-hidden">
|
|
||||||
<div className="text-sm text-txt-primary whitespace-nowrap overflow-hidden text-ellipsis">
|
|
||||||
{version.publishedBy?.fullName ?? props.document.owner.fullName}
|
|
||||||
</div>
|
|
||||||
<div className="text-xs text-txt-secondary whitespace-nowrap overflow-hidden text-ellipsis">
|
|
||||||
{dateTimeFormat(version.publishedAt ?? version.updatedAt)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
/**
|
|
||||||
* @generated SignedSource<<dc19ab6bedc70a5b4546a2edb922b97f>>
|
|
||||||
* @lightSyntaxTransform
|
|
||||||
* @nogrep
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* tslint:disable */
|
|
||||||
/* eslint-disable */
|
|
||||||
// @ts-nocheck
|
|
||||||
|
|
||||||
import { ReaderFragment } from 'relay-runtime';
|
|
||||||
export type DocumentStatus = "DRAFT" | "PUBLISHED";
|
|
||||||
import { FragmentRefs } from "relay-runtime";
|
|
||||||
export type DocumentSignaturesDialog_version$data = {
|
|
||||||
readonly publishedAt: any | null | undefined;
|
|
||||||
readonly status: DocumentStatus;
|
|
||||||
readonly updatedAt: any;
|
|
||||||
readonly version: number;
|
|
||||||
readonly " $fragmentType": "DocumentSignaturesDialog_version";
|
|
||||||
};
|
|
||||||
export type DocumentSignaturesDialog_version$key = {
|
|
||||||
readonly " $data"?: DocumentSignaturesDialog_version$data;
|
|
||||||
readonly " $fragmentSpreads": FragmentRefs<"DocumentSignaturesDialog_version">;
|
|
||||||
};
|
|
||||||
|
|
||||||
const node: ReaderFragment = {
|
|
||||||
"argumentDefinitions": [],
|
|
||||||
"kind": "Fragment",
|
|
||||||
"metadata": null,
|
|
||||||
"name": "DocumentSignaturesDialog_version",
|
|
||||||
"selections": [
|
|
||||||
{
|
|
||||||
"alias": null,
|
|
||||||
"args": null,
|
|
||||||
"kind": "ScalarField",
|
|
||||||
"name": "version",
|
|
||||||
"storageKey": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"alias": null,
|
|
||||||
"args": null,
|
|
||||||
"kind": "ScalarField",
|
|
||||||
"name": "status",
|
|
||||||
"storageKey": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"alias": null,
|
|
||||||
"args": null,
|
|
||||||
"kind": "ScalarField",
|
|
||||||
"name": "publishedAt",
|
|
||||||
"storageKey": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"alias": null,
|
|
||||||
"args": null,
|
|
||||||
"kind": "ScalarField",
|
|
||||||
"name": "updatedAt",
|
|
||||||
"storageKey": null
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"type": "DocumentVersion",
|
|
||||||
"abstractKey": null
|
|
||||||
};
|
|
||||||
|
|
||||||
(node as any).hash = "564746ce584a834e25a5a341155d6b2c";
|
|
||||||
|
|
||||||
export default node;
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
/**
|
|
||||||
* @generated SignedSource<<06126b60bc37d8fbbb9a475af6d5d29d>>
|
|
||||||
* @lightSyntaxTransform
|
|
||||||
* @nogrep
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* tslint:disable */
|
|
||||||
/* eslint-disable */
|
|
||||||
// @ts-nocheck
|
|
||||||
|
|
||||||
import { ConcreteRequest } from 'relay-runtime';
|
|
||||||
export type DocumentVersionHistoryDialogExportPDFMutation$variables = {
|
|
||||||
documentVersionId: string;
|
|
||||||
};
|
|
||||||
export type DocumentVersionHistoryDialogExportPDFMutation$data = {
|
|
||||||
readonly exportDocumentVersionPDF: {
|
|
||||||
readonly data: string;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
export type DocumentVersionHistoryDialogExportPDFMutation = {
|
|
||||||
response: DocumentVersionHistoryDialogExportPDFMutation$data;
|
|
||||||
variables: DocumentVersionHistoryDialogExportPDFMutation$variables;
|
|
||||||
};
|
|
||||||
|
|
||||||
const node: ConcreteRequest = (function(){
|
|
||||||
var v0 = [
|
|
||||||
{
|
|
||||||
"defaultValue": null,
|
|
||||||
"kind": "LocalArgument",
|
|
||||||
"name": "documentVersionId"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
v1 = [
|
|
||||||
{
|
|
||||||
"alias": null,
|
|
||||||
"args": [
|
|
||||||
{
|
|
||||||
"fields": [
|
|
||||||
{
|
|
||||||
"kind": "Variable",
|
|
||||||
"name": "documentVersionId",
|
|
||||||
"variableName": "documentVersionId"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"kind": "ObjectValue",
|
|
||||||
"name": "input"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"concreteType": "ExportDocumentVersionPDFPayload",
|
|
||||||
"kind": "LinkedField",
|
|
||||||
"name": "exportDocumentVersionPDF",
|
|
||||||
"plural": false,
|
|
||||||
"selections": [
|
|
||||||
{
|
|
||||||
"alias": null,
|
|
||||||
"args": null,
|
|
||||||
"kind": "ScalarField",
|
|
||||||
"name": "data",
|
|
||||||
"storageKey": null
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"storageKey": null
|
|
||||||
}
|
|
||||||
];
|
|
||||||
return {
|
|
||||||
"fragment": {
|
|
||||||
"argumentDefinitions": (v0/*: any*/),
|
|
||||||
"kind": "Fragment",
|
|
||||||
"metadata": null,
|
|
||||||
"name": "DocumentVersionHistoryDialogExportPDFMutation",
|
|
||||||
"selections": (v1/*: any*/),
|
|
||||||
"type": "Mutation",
|
|
||||||
"abstractKey": null
|
|
||||||
},
|
|
||||||
"kind": "Request",
|
|
||||||
"operation": {
|
|
||||||
"argumentDefinitions": (v0/*: any*/),
|
|
||||||
"kind": "Operation",
|
|
||||||
"name": "DocumentVersionHistoryDialogExportPDFMutation",
|
|
||||||
"selections": (v1/*: any*/)
|
|
||||||
},
|
|
||||||
"params": {
|
|
||||||
"cacheID": "45204e42c1018e00dcceaae84eb9a5f7",
|
|
||||||
"id": null,
|
|
||||||
"metadata": {},
|
|
||||||
"name": "DocumentVersionHistoryDialogExportPDFMutation",
|
|
||||||
"operationKind": "mutation",
|
|
||||||
"text": "mutation DocumentVersionHistoryDialogExportPDFMutation(\n $documentVersionId: ID!\n) {\n exportDocumentVersionPDF(input: {documentVersionId: $documentVersionId}) {\n data\n }\n}\n"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
})();
|
|
||||||
|
|
||||||
(node as any).hash = "587bcc8bb6cf72e64a5eca18769ac432";
|
|
||||||
|
|
||||||
export default node;
|
|
||||||
@@ -1,112 +0,0 @@
|
|||||||
/**
|
|
||||||
* @generated SignedSource<<dd89afd13dd17b29485a3fdaf840cd42>>
|
|
||||||
* @lightSyntaxTransform
|
|
||||||
* @nogrep
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* tslint:disable */
|
|
||||||
/* eslint-disable */
|
|
||||||
// @ts-nocheck
|
|
||||||
|
|
||||||
import { ReaderFragment } from 'relay-runtime';
|
|
||||||
export type DocumentStatus = "DRAFT" | "PUBLISHED";
|
|
||||||
import { FragmentRefs } from "relay-runtime";
|
|
||||||
export type DocumentVersionHistoryDialogFragment$data = {
|
|
||||||
readonly changelog: string;
|
|
||||||
readonly content: string;
|
|
||||||
readonly id: string;
|
|
||||||
readonly publishedAt: any | null | undefined;
|
|
||||||
readonly publishedBy: {
|
|
||||||
readonly fullName: string;
|
|
||||||
} | null | undefined;
|
|
||||||
readonly status: DocumentStatus;
|
|
||||||
readonly updatedAt: any;
|
|
||||||
readonly version: number;
|
|
||||||
readonly " $fragmentType": "DocumentVersionHistoryDialogFragment";
|
|
||||||
};
|
|
||||||
export type DocumentVersionHistoryDialogFragment$key = {
|
|
||||||
readonly " $data"?: DocumentVersionHistoryDialogFragment$data;
|
|
||||||
readonly " $fragmentSpreads": FragmentRefs<"DocumentVersionHistoryDialogFragment">;
|
|
||||||
};
|
|
||||||
|
|
||||||
const node: ReaderFragment = {
|
|
||||||
"argumentDefinitions": [],
|
|
||||||
"kind": "Fragment",
|
|
||||||
"metadata": null,
|
|
||||||
"name": "DocumentVersionHistoryDialogFragment",
|
|
||||||
"selections": [
|
|
||||||
{
|
|
||||||
"alias": null,
|
|
||||||
"args": null,
|
|
||||||
"kind": "ScalarField",
|
|
||||||
"name": "id",
|
|
||||||
"storageKey": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"alias": null,
|
|
||||||
"args": null,
|
|
||||||
"kind": "ScalarField",
|
|
||||||
"name": "version",
|
|
||||||
"storageKey": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"alias": null,
|
|
||||||
"args": null,
|
|
||||||
"kind": "ScalarField",
|
|
||||||
"name": "status",
|
|
||||||
"storageKey": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"alias": null,
|
|
||||||
"args": null,
|
|
||||||
"kind": "ScalarField",
|
|
||||||
"name": "content",
|
|
||||||
"storageKey": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"alias": null,
|
|
||||||
"args": null,
|
|
||||||
"kind": "ScalarField",
|
|
||||||
"name": "changelog",
|
|
||||||
"storageKey": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"alias": null,
|
|
||||||
"args": null,
|
|
||||||
"kind": "ScalarField",
|
|
||||||
"name": "publishedAt",
|
|
||||||
"storageKey": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"alias": null,
|
|
||||||
"args": null,
|
|
||||||
"kind": "ScalarField",
|
|
||||||
"name": "updatedAt",
|
|
||||||
"storageKey": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"alias": null,
|
|
||||||
"args": null,
|
|
||||||
"concreteType": "People",
|
|
||||||
"kind": "LinkedField",
|
|
||||||
"name": "publishedBy",
|
|
||||||
"plural": false,
|
|
||||||
"selections": [
|
|
||||||
{
|
|
||||||
"alias": null,
|
|
||||||
"args": null,
|
|
||||||
"kind": "ScalarField",
|
|
||||||
"name": "fullName",
|
|
||||||
"storageKey": null
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"storageKey": null
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"type": "DocumentVersion",
|
|
||||||
"abstractKey": null
|
|
||||||
};
|
|
||||||
|
|
||||||
(node as any).hash = "947253287039236aa413ee33879a6e4b";
|
|
||||||
|
|
||||||
export default node;
|
|
||||||
@@ -4,12 +4,12 @@ import type { NodeOf } from "/types";
|
|||||||
import { Markdown } from "@probo/ui";
|
import { Markdown } from "@probo/ui";
|
||||||
|
|
||||||
export default function DocumentDescriptionTab() {
|
export default function DocumentDescriptionTab() {
|
||||||
const { lastVersion } = useOutletContext<{
|
const { version } = useOutletContext<{
|
||||||
lastVersion: NodeOf<DocumentDetailPageDocumentFragment$data["versions"]>;
|
version: NodeOf<DocumentDetailPageDocumentFragment$data["versions"]>;
|
||||||
}>();
|
}>();
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Markdown content={lastVersion.content} />
|
<Markdown content={version.content} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,119 +2,34 @@ import {
|
|||||||
Avatar,
|
Avatar,
|
||||||
Badge,
|
Badge,
|
||||||
Button,
|
Button,
|
||||||
Dialog,
|
|
||||||
DialogContent,
|
|
||||||
DialogFooter,
|
|
||||||
DialogTitle,
|
|
||||||
IconCircleCheck,
|
IconCircleCheck,
|
||||||
IconClock,
|
IconClock,
|
||||||
DocumentVersionBadge,
|
|
||||||
Spinner,
|
Spinner,
|
||||||
} from "@probo/ui";
|
} from "@probo/ui";
|
||||||
import { useTranslate } from "@probo/i18n";
|
import { useTranslate } from "@probo/i18n";
|
||||||
import { useState, type ReactNode, Suspense } from "react";
|
import { Suspense } from "react";
|
||||||
import clsx from "clsx";
|
|
||||||
import type { ItemOf, NodeOf } from "/types";
|
import type { ItemOf, NodeOf } from "/types";
|
||||||
import { graphql, useFragment } from "react-relay";
|
import { graphql, useFragment } from "react-relay";
|
||||||
import type { DocumentSignaturesDialog_version$key } from "./__generated__/DocumentSignaturesDialog_version.graphql";
|
|
||||||
import type { DocumentSignaturesDialog_signature$key } from "./__generated__/DocumentSignaturesDialog_signature.graphql";
|
|
||||||
import { usePeople } from "/hooks/graph/PeopleGraph.ts";
|
import { usePeople } from "/hooks/graph/PeopleGraph.ts";
|
||||||
import { useOrganizationId } from "/hooks/useOrganizationId.ts";
|
import { useOrganizationId } from "/hooks/useOrganizationId.ts";
|
||||||
import { useMutationWithToasts } from "/hooks/useMutationWithToasts.ts";
|
import { useMutationWithToasts } from "/hooks/useMutationWithToasts.ts";
|
||||||
import { sprintf } from "@probo/helpers";
|
import { sprintf } from "@probo/helpers";
|
||||||
import type { DocumentDetailPageDocumentFragment$data } from "../__generated__/DocumentDetailPageDocumentFragment.graphql";
|
import type { DocumentDetailPageDocumentFragment$data } from "../__generated__/DocumentDetailPageDocumentFragment.graphql";
|
||||||
|
import { useOutletContext } from "react-router";
|
||||||
type Props = {
|
import type { DocumentSignaturesTab_signature$key } from "/pages/organizations/documents/tabs/__generated__/DocumentSignaturesTab_signature.graphql.ts";
|
||||||
document: DocumentDetailPageDocumentFragment$data;
|
|
||||||
children?: ReactNode;
|
|
||||||
};
|
|
||||||
|
|
||||||
type Version = NodeOf<DocumentDetailPageDocumentFragment$data["versions"]>;
|
type Version = NodeOf<DocumentDetailPageDocumentFragment$data["versions"]>;
|
||||||
|
|
||||||
export function DocumentSignaturesDialog(props: Props) {
|
export default function DocumentSignaturesTab() {
|
||||||
const { __ } = useTranslate();
|
const { version } = useOutletContext<{ version: Version }>();
|
||||||
const versions = props.document.versions.edges.map((edge) => edge.node);
|
if (!version) {
|
||||||
const [selectedVersionId, setSelectedVersionId] = useState<string>(
|
|
||||||
versions[0].id,
|
|
||||||
);
|
|
||||||
const selectedVersion = versions.find((v) => v.id === selectedVersionId);
|
|
||||||
|
|
||||||
if (!selectedVersion) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog trigger={props.children}>
|
|
||||||
<DialogContent className="flex" scrollableChildren>
|
|
||||||
<aside className="p-6 overflow-y-auto w-60 flex-none space-y-2">
|
|
||||||
<div className="text-base text-txt-primary font-medium mb-4">
|
|
||||||
{__("Version History")}
|
|
||||||
</div>
|
|
||||||
{versions.map((version) => (
|
|
||||||
<VersionItem
|
|
||||||
key={version.id}
|
|
||||||
version={version}
|
|
||||||
active={selectedVersionId === version.id}
|
|
||||||
onSelect={setSelectedVersionId}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</aside>
|
|
||||||
<main className="flex-1 px-12 py-8">
|
|
||||||
<DialogTitle className="text-2xl font-bold text-txt-primary mb-4">
|
|
||||||
{__("Signatures")}
|
|
||||||
</DialogTitle>
|
|
||||||
<p className="text-sm text-txt-secondary mb-4">
|
|
||||||
{__(
|
|
||||||
"Click request to ask for a signature from people in your organization.",
|
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
<Suspense fallback={<Spinner centered />}>
|
<Suspense fallback={<Spinner centered />}>
|
||||||
<SignatureList version={selectedVersion} />
|
<SignatureList version={version} />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</main>
|
|
||||||
</DialogContent>
|
|
||||||
<DialogFooter />
|
|
||||||
</Dialog>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const versionFragment = graphql`
|
|
||||||
fragment DocumentSignaturesDialog_version on DocumentVersion {
|
|
||||||
version
|
|
||||||
status
|
|
||||||
publishedAt
|
|
||||||
updatedAt
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
function VersionItem(props: {
|
|
||||||
version: Version;
|
|
||||||
active?: boolean;
|
|
||||||
onSelect: (v: string) => void;
|
|
||||||
}) {
|
|
||||||
const { dateTimeFormat, __ } = useTranslate();
|
|
||||||
const version = useFragment<DocumentSignaturesDialog_version$key>(
|
|
||||||
versionFragment,
|
|
||||||
props.version,
|
|
||||||
);
|
|
||||||
return (
|
|
||||||
<button
|
|
||||||
onClick={() => props.onSelect(props.version.id)}
|
|
||||||
className={clsx(
|
|
||||||
"text-start space-y-1 w-full overflow-hidden py-2 px-3 w-full hover:bg-tertiary-hover cursor-pointer rounded",
|
|
||||||
props.active && "bg-tertiary-pressed",
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<div className="flex gap-1 text-sm text-txt-primary">
|
|
||||||
<span>
|
|
||||||
{__("Version")} {version.version}
|
|
||||||
</span>
|
|
||||||
<DocumentVersionBadge state={version.status} />
|
|
||||||
</div>
|
|
||||||
<div className="text-xs text-txt-secondary">
|
|
||||||
{dateTimeFormat(version.publishedAt ?? version.updatedAt)}
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,7 +69,7 @@ function SignatureList(props: { version: Version }) {
|
|||||||
* Fragments
|
* Fragments
|
||||||
*/
|
*/
|
||||||
const signatureFragment = graphql`
|
const signatureFragment = graphql`
|
||||||
fragment DocumentSignaturesDialog_signature on DocumentVersionSignature {
|
fragment DocumentSignaturesTab_signature on DocumentVersionSignature {
|
||||||
id
|
id
|
||||||
state
|
state
|
||||||
signedAt
|
signedAt
|
||||||
@@ -170,7 +85,7 @@ const signatureFragment = graphql`
|
|||||||
* Mutations
|
* Mutations
|
||||||
*/
|
*/
|
||||||
const requestSignatureMutation = graphql`
|
const requestSignatureMutation = graphql`
|
||||||
mutation DocumentSignaturesDialog_requestSignatureMutation(
|
mutation DocumentSignaturesTab_requestSignatureMutation(
|
||||||
$input: RequestSignatureInput!
|
$input: RequestSignatureInput!
|
||||||
$connections: [ID!]!
|
$connections: [ID!]!
|
||||||
) {
|
) {
|
||||||
@@ -182,14 +97,14 @@ const requestSignatureMutation = graphql`
|
|||||||
signedBy {
|
signedBy {
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
...DocumentSignaturesDialog_signature
|
...DocumentSignaturesTab_signature
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
const cancelSignatureMutation = graphql`
|
const cancelSignatureMutation = graphql`
|
||||||
mutation DocumentSignaturesDialog_cancelSignatureMutation(
|
mutation DocumentSignaturesTab_cancelSignatureMutation(
|
||||||
$input: CancelSignatureRequestInput!
|
$input: CancelSignatureRequestInput!
|
||||||
$connections: [ID!]!
|
$connections: [ID!]!
|
||||||
) {
|
) {
|
||||||
@@ -201,7 +116,7 @@ const cancelSignatureMutation = graphql`
|
|||||||
|
|
||||||
function SignatureItem(props: {
|
function SignatureItem(props: {
|
||||||
versionId: string;
|
versionId: string;
|
||||||
signature?: DocumentSignaturesDialog_signature$key;
|
signature?: DocumentSignaturesTab_signature$key;
|
||||||
people: ItemOf<ReturnType<typeof usePeople>>;
|
people: ItemOf<ReturnType<typeof usePeople>>;
|
||||||
connectionId: string;
|
connectionId: string;
|
||||||
signable: boolean;
|
signable: boolean;
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* @generated SignedSource<<9f55e528d2b6366114a1852c9beaf1b0>>
|
* @generated SignedSource<<0a17c03011fe74b75a90dbeae02ab2ef>>
|
||||||
* @lightSyntaxTransform
|
* @lightSyntaxTransform
|
||||||
* @nogrep
|
* @nogrep
|
||||||
*/
|
*/
|
||||||
@@ -12,18 +12,18 @@ import { ConcreteRequest } from 'relay-runtime';
|
|||||||
export type CancelSignatureRequestInput = {
|
export type CancelSignatureRequestInput = {
|
||||||
documentVersionSignatureId: string;
|
documentVersionSignatureId: string;
|
||||||
};
|
};
|
||||||
export type DocumentSignaturesDialog_cancelSignatureMutation$variables = {
|
export type DocumentSignaturesTab_cancelSignatureMutation$variables = {
|
||||||
connections: ReadonlyArray<string>;
|
connections: ReadonlyArray<string>;
|
||||||
input: CancelSignatureRequestInput;
|
input: CancelSignatureRequestInput;
|
||||||
};
|
};
|
||||||
export type DocumentSignaturesDialog_cancelSignatureMutation$data = {
|
export type DocumentSignaturesTab_cancelSignatureMutation$data = {
|
||||||
readonly cancelSignatureRequest: {
|
readonly cancelSignatureRequest: {
|
||||||
readonly deletedDocumentVersionSignatureId: string;
|
readonly deletedDocumentVersionSignatureId: string;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
export type DocumentSignaturesDialog_cancelSignatureMutation = {
|
export type DocumentSignaturesTab_cancelSignatureMutation = {
|
||||||
response: DocumentSignaturesDialog_cancelSignatureMutation$data;
|
response: DocumentSignaturesTab_cancelSignatureMutation$data;
|
||||||
variables: DocumentSignaturesDialog_cancelSignatureMutation$variables;
|
variables: DocumentSignaturesTab_cancelSignatureMutation$variables;
|
||||||
};
|
};
|
||||||
|
|
||||||
const node: ConcreteRequest = (function(){
|
const node: ConcreteRequest = (function(){
|
||||||
@@ -59,7 +59,7 @@ return {
|
|||||||
],
|
],
|
||||||
"kind": "Fragment",
|
"kind": "Fragment",
|
||||||
"metadata": null,
|
"metadata": null,
|
||||||
"name": "DocumentSignaturesDialog_cancelSignatureMutation",
|
"name": "DocumentSignaturesTab_cancelSignatureMutation",
|
||||||
"selections": [
|
"selections": [
|
||||||
{
|
{
|
||||||
"alias": null,
|
"alias": null,
|
||||||
@@ -84,7 +84,7 @@ return {
|
|||||||
(v0/*: any*/)
|
(v0/*: any*/)
|
||||||
],
|
],
|
||||||
"kind": "Operation",
|
"kind": "Operation",
|
||||||
"name": "DocumentSignaturesDialog_cancelSignatureMutation",
|
"name": "DocumentSignaturesTab_cancelSignatureMutation",
|
||||||
"selections": [
|
"selections": [
|
||||||
{
|
{
|
||||||
"alias": null,
|
"alias": null,
|
||||||
@@ -117,16 +117,16 @@ return {
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"params": {
|
"params": {
|
||||||
"cacheID": "81d10822e563ceb7ebcd4fdb5d19e8f5",
|
"cacheID": "c57722e2b57faca64eea4950864e034d",
|
||||||
"id": null,
|
"id": null,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"name": "DocumentSignaturesDialog_cancelSignatureMutation",
|
"name": "DocumentSignaturesTab_cancelSignatureMutation",
|
||||||
"operationKind": "mutation",
|
"operationKind": "mutation",
|
||||||
"text": "mutation DocumentSignaturesDialog_cancelSignatureMutation(\n $input: CancelSignatureRequestInput!\n) {\n cancelSignatureRequest(input: $input) {\n deletedDocumentVersionSignatureId\n }\n}\n"
|
"text": "mutation DocumentSignaturesTab_cancelSignatureMutation(\n $input: CancelSignatureRequestInput!\n) {\n cancelSignatureRequest(input: $input) {\n deletedDocumentVersionSignatureId\n }\n}\n"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
(node as any).hash = "0cbec91e7ba7cc09d2bbf7169e98f30c";
|
(node as any).hash = "9f5d3ff869ec6f91285140285248e824";
|
||||||
|
|
||||||
export default node;
|
export default node;
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* @generated SignedSource<<fa604130d8725651770072b638fc5926>>
|
* @generated SignedSource<<de274a51c1b339789540a969b0ebe507>>
|
||||||
* @lightSyntaxTransform
|
* @lightSyntaxTransform
|
||||||
* @nogrep
|
* @nogrep
|
||||||
*/
|
*/
|
||||||
@@ -15,11 +15,11 @@ export type RequestSignatureInput = {
|
|||||||
documentVersionId: string;
|
documentVersionId: string;
|
||||||
signatoryId: string;
|
signatoryId: string;
|
||||||
};
|
};
|
||||||
export type DocumentSignaturesDialog_requestSignatureMutation$variables = {
|
export type DocumentSignaturesTab_requestSignatureMutation$variables = {
|
||||||
connections: ReadonlyArray<string>;
|
connections: ReadonlyArray<string>;
|
||||||
input: RequestSignatureInput;
|
input: RequestSignatureInput;
|
||||||
};
|
};
|
||||||
export type DocumentSignaturesDialog_requestSignatureMutation$data = {
|
export type DocumentSignaturesTab_requestSignatureMutation$data = {
|
||||||
readonly requestSignature: {
|
readonly requestSignature: {
|
||||||
readonly documentVersionSignatureEdge: {
|
readonly documentVersionSignatureEdge: {
|
||||||
readonly node: {
|
readonly node: {
|
||||||
@@ -28,14 +28,14 @@ export type DocumentSignaturesDialog_requestSignatureMutation$data = {
|
|||||||
readonly id: string;
|
readonly id: string;
|
||||||
};
|
};
|
||||||
readonly state: DocumentVersionSignatureState;
|
readonly state: DocumentVersionSignatureState;
|
||||||
readonly " $fragmentSpreads": FragmentRefs<"DocumentSignaturesDialog_signature">;
|
readonly " $fragmentSpreads": FragmentRefs<"DocumentSignaturesTab_signature">;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
export type DocumentSignaturesDialog_requestSignatureMutation = {
|
export type DocumentSignaturesTab_requestSignatureMutation = {
|
||||||
response: DocumentSignaturesDialog_requestSignatureMutation$data;
|
response: DocumentSignaturesTab_requestSignatureMutation$data;
|
||||||
variables: DocumentSignaturesDialog_requestSignatureMutation$variables;
|
variables: DocumentSignaturesTab_requestSignatureMutation$variables;
|
||||||
};
|
};
|
||||||
|
|
||||||
const node: ConcreteRequest = (function(){
|
const node: ConcreteRequest = (function(){
|
||||||
@@ -78,7 +78,7 @@ return {
|
|||||||
],
|
],
|
||||||
"kind": "Fragment",
|
"kind": "Fragment",
|
||||||
"metadata": null,
|
"metadata": null,
|
||||||
"name": "DocumentSignaturesDialog_requestSignatureMutation",
|
"name": "DocumentSignaturesTab_requestSignatureMutation",
|
||||||
"selections": [
|
"selections": [
|
||||||
{
|
{
|
||||||
"alias": null,
|
"alias": null,
|
||||||
@@ -121,7 +121,7 @@ return {
|
|||||||
{
|
{
|
||||||
"args": null,
|
"args": null,
|
||||||
"kind": "FragmentSpread",
|
"kind": "FragmentSpread",
|
||||||
"name": "DocumentSignaturesDialog_signature"
|
"name": "DocumentSignaturesTab_signature"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
@@ -143,7 +143,7 @@ return {
|
|||||||
(v0/*: any*/)
|
(v0/*: any*/)
|
||||||
],
|
],
|
||||||
"kind": "Operation",
|
"kind": "Operation",
|
||||||
"name": "DocumentSignaturesDialog_requestSignatureMutation",
|
"name": "DocumentSignaturesTab_requestSignatureMutation",
|
||||||
"selections": [
|
"selections": [
|
||||||
{
|
{
|
||||||
"alias": null,
|
"alias": null,
|
||||||
@@ -239,16 +239,16 @@ return {
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"params": {
|
"params": {
|
||||||
"cacheID": "6a5413db69a07e0eef99245e949cf4b2",
|
"cacheID": "e582b90bdad831d64741b45de17f45b5",
|
||||||
"id": null,
|
"id": null,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"name": "DocumentSignaturesDialog_requestSignatureMutation",
|
"name": "DocumentSignaturesTab_requestSignatureMutation",
|
||||||
"operationKind": "mutation",
|
"operationKind": "mutation",
|
||||||
"text": "mutation DocumentSignaturesDialog_requestSignatureMutation(\n $input: RequestSignatureInput!\n) {\n requestSignature(input: $input) {\n documentVersionSignatureEdge {\n node {\n id\n state\n signedBy {\n id\n }\n ...DocumentSignaturesDialog_signature\n }\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"
|
"text": "mutation DocumentSignaturesTab_requestSignatureMutation(\n $input: RequestSignatureInput!\n) {\n requestSignature(input: $input) {\n documentVersionSignatureEdge {\n node {\n id\n state\n signedBy {\n id\n }\n ...DocumentSignaturesTab_signature\n }\n }\n }\n}\n\nfragment DocumentSignaturesTab_signature on DocumentVersionSignature {\n id\n state\n signedAt\n requestedAt\n signedBy {\n fullName\n primaryEmailAddress\n id\n }\n}\n"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
(node as any).hash = "6c51f681411cd3de162c78a3db20ec30";
|
(node as any).hash = "f0b9fc6ff9db455e9079bf7e45fcd603";
|
||||||
|
|
||||||
export default node;
|
export default node;
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* @generated SignedSource<<71e44f5802c3eaba88dde399af20ed89>>
|
* @generated SignedSource<<e3ddbde4beb849447cb819f8991245a7>>
|
||||||
* @lightSyntaxTransform
|
* @lightSyntaxTransform
|
||||||
* @nogrep
|
* @nogrep
|
||||||
*/
|
*/
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
import { ReaderFragment } from 'relay-runtime';
|
import { ReaderFragment } from 'relay-runtime';
|
||||||
export type DocumentVersionSignatureState = "REQUESTED" | "SIGNED";
|
export type DocumentVersionSignatureState = "REQUESTED" | "SIGNED";
|
||||||
import { FragmentRefs } from "relay-runtime";
|
import { FragmentRefs } from "relay-runtime";
|
||||||
export type DocumentSignaturesDialog_signature$data = {
|
export type DocumentSignaturesTab_signature$data = {
|
||||||
readonly id: string;
|
readonly id: string;
|
||||||
readonly requestedAt: any;
|
readonly requestedAt: any;
|
||||||
readonly signedAt: any | null | undefined;
|
readonly signedAt: any | null | undefined;
|
||||||
@@ -20,18 +20,18 @@ export type DocumentSignaturesDialog_signature$data = {
|
|||||||
readonly primaryEmailAddress: string;
|
readonly primaryEmailAddress: string;
|
||||||
};
|
};
|
||||||
readonly state: DocumentVersionSignatureState;
|
readonly state: DocumentVersionSignatureState;
|
||||||
readonly " $fragmentType": "DocumentSignaturesDialog_signature";
|
readonly " $fragmentType": "DocumentSignaturesTab_signature";
|
||||||
};
|
};
|
||||||
export type DocumentSignaturesDialog_signature$key = {
|
export type DocumentSignaturesTab_signature$key = {
|
||||||
readonly " $data"?: DocumentSignaturesDialog_signature$data;
|
readonly " $data"?: DocumentSignaturesTab_signature$data;
|
||||||
readonly " $fragmentSpreads": FragmentRefs<"DocumentSignaturesDialog_signature">;
|
readonly " $fragmentSpreads": FragmentRefs<"DocumentSignaturesTab_signature">;
|
||||||
};
|
};
|
||||||
|
|
||||||
const node: ReaderFragment = {
|
const node: ReaderFragment = {
|
||||||
"argumentDefinitions": [],
|
"argumentDefinitions": [],
|
||||||
"kind": "Fragment",
|
"kind": "Fragment",
|
||||||
"metadata": null,
|
"metadata": null,
|
||||||
"name": "DocumentSignaturesDialog_signature",
|
"name": "DocumentSignaturesTab_signature",
|
||||||
"selections": [
|
"selections": [
|
||||||
{
|
{
|
||||||
"alias": null,
|
"alias": null,
|
||||||
@@ -91,6 +91,6 @@ const node: ReaderFragment = {
|
|||||||
"abstractKey": null
|
"abstractKey": null
|
||||||
};
|
};
|
||||||
|
|
||||||
(node as any).hash = "0db843d4ebd21f44a3d126e85da8cf26";
|
(node as any).hash = "b07437b34548744fef4ed78d4f1f0a62";
|
||||||
|
|
||||||
export default node;
|
export default node;
|
||||||
@@ -9,6 +9,46 @@ import { redirect } from "react-router";
|
|||||||
import { lazy } from "@probo/react-lazy";
|
import { lazy } from "@probo/react-lazy";
|
||||||
import { LinkCardSkeleton } from "/components/skeletons/LinkCardSkeleton";
|
import { LinkCardSkeleton } from "/components/skeletons/LinkCardSkeleton";
|
||||||
|
|
||||||
|
const documentTabs = (prefix: string) => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
path: `${prefix}`,
|
||||||
|
queryLoader: ({ organizationId, documentId }) => {
|
||||||
|
throw redirect(
|
||||||
|
`/organizations/${organizationId}/documents/${documentId}/description`,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
Component: Fragment,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: `${prefix}description`,
|
||||||
|
fallback: LinkCardSkeleton,
|
||||||
|
Component: lazy(
|
||||||
|
() =>
|
||||||
|
import(
|
||||||
|
"../pages/organizations/documents/tabs/DocumentDescriptionTab"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: `${prefix}controls`,
|
||||||
|
fallback: LinkCardSkeleton,
|
||||||
|
Component: lazy(
|
||||||
|
() =>
|
||||||
|
import("../pages/organizations/documents/tabs/DocumentControlsTab"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: `${prefix}signatures`,
|
||||||
|
fallback: LinkCardSkeleton,
|
||||||
|
Component: lazy(
|
||||||
|
() =>
|
||||||
|
import("../pages/organizations/documents/tabs/DocumentSignaturesTab"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
] satisfies AppRoute[];
|
||||||
|
};
|
||||||
|
|
||||||
export const documentsRoutes = [
|
export const documentsRoutes = [
|
||||||
{
|
{
|
||||||
path: "documents",
|
path: "documents",
|
||||||
@@ -16,7 +56,7 @@ export const documentsRoutes = [
|
|||||||
queryLoader: ({ organizationId }) =>
|
queryLoader: ({ organizationId }) =>
|
||||||
loadQuery(relayEnvironment, documentsQuery, { organizationId }),
|
loadQuery(relayEnvironment, documentsQuery, { organizationId }),
|
||||||
Component: lazy(
|
Component: lazy(
|
||||||
() => import("/pages/organizations/documents/DocumentsPage")
|
() => import("/pages/organizations/documents/DocumentsPage"),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -25,36 +65,8 @@ export const documentsRoutes = [
|
|||||||
queryLoader: ({ documentId }) =>
|
queryLoader: ({ documentId }) =>
|
||||||
loadQuery(relayEnvironment, documentNodeQuery, { documentId }),
|
loadQuery(relayEnvironment, documentNodeQuery, { documentId }),
|
||||||
Component: lazy(
|
Component: lazy(
|
||||||
() => import("../pages/organizations/documents/DocumentDetailPage")
|
() => import("../pages/organizations/documents/DocumentDetailPage"),
|
||||||
),
|
),
|
||||||
children: [
|
children: [...documentTabs(""), ...documentTabs("versions/:versionId/")],
|
||||||
{
|
|
||||||
path: "",
|
|
||||||
queryLoader: ({ organizationId, documentId }) => {
|
|
||||||
throw redirect(
|
|
||||||
`/organizations/${organizationId}/documents/${documentId}/description`
|
|
||||||
);
|
|
||||||
},
|
|
||||||
Component: Fragment,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "description",
|
|
||||||
fallback: LinkCardSkeleton,
|
|
||||||
Component: lazy(
|
|
||||||
() =>
|
|
||||||
import(
|
|
||||||
"../pages/organizations/documents/tabs/DocumentDescriptionTab"
|
|
||||||
)
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "controls",
|
|
||||||
fallback: LinkCardSkeleton,
|
|
||||||
Component: lazy(
|
|
||||||
() =>
|
|
||||||
import("../pages/organizations/documents/tabs/DocumentControlsTab")
|
|
||||||
),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
] satisfies AppRoute[];
|
] satisfies AppRoute[];
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
|
|||||||
import { clsx } from "clsx";
|
import { clsx } from "clsx";
|
||||||
import type { IconProps } from "../Icons/type.ts";
|
import type { IconProps } from "../Icons/type.ts";
|
||||||
import { tv } from "tailwind-variants";
|
import { tv } from "tailwind-variants";
|
||||||
import { IconDotGrid1x3Horizontal } from "../Icons/IconDotGrid1x3Horizontal.tsx";
|
import { IconDotGrid1x3Horizontal } from "../Icons";
|
||||||
import { Button } from "../Button/Button.tsx";
|
import { Button } from "../Button/Button.tsx";
|
||||||
|
|
||||||
type Props = PropsWithChildren<{
|
type Props = PropsWithChildren<{
|
||||||
@@ -24,6 +24,10 @@ export function Dropdown({ children, toggle, className, open }: Props) {
|
|||||||
)}
|
)}
|
||||||
<DropdownMenu.Portal>
|
<DropdownMenu.Portal>
|
||||||
<DropdownMenu.Content
|
<DropdownMenu.Content
|
||||||
|
style={{
|
||||||
|
maxHeight:
|
||||||
|
"var(--radix-dropdown-menu-content-available-height)",
|
||||||
|
}}
|
||||||
className={dropdown({ className })}
|
className={dropdown({ className })}
|
||||||
sideOffset={5}
|
sideOffset={5}
|
||||||
>
|
>
|
||||||
|
|||||||
Reference in New Issue
Block a user