Remove version update dialog

Signed-off-by: Émile Ré <emile@getprobo.com>
This commit is contained in:
Émile Ré
2026-04-01 10:58:48 +04:00
parent 3d1d794a84
commit 3cba683707
3 changed files with 71 additions and 253 deletions

View File

@@ -21,6 +21,7 @@ import { useNavigate, useParams } from "react-router";
import { ConnectionHandler, graphql } from "relay-runtime";
import type { DocumentActionsDropdown_archiveMutation } from "#/__generated__/core/DocumentActionsDropdown_archiveMutation.graphql";
import type { DocumentActionsDropdown_createDraftMutation } from "#/__generated__/core/DocumentActionsDropdown_createDraftMutation.graphql";
import type { DocumentActionsDropdown_documentFragment$key } from "#/__generated__/core/DocumentActionsDropdown_documentFragment.graphql";
import type { DocumentActionsDropdown_unarchiveMutation } from "#/__generated__/core/DocumentActionsDropdown_unarchiveMutation.graphql";
import type { DocumentActionsDropdown_versionFragment$key } from "#/__generated__/core/DocumentActionsDropdown_versionFragment.graphql";
@@ -31,8 +32,6 @@ import { useMutationWithToasts } from "#/hooks/useMutationWithToasts";
import { useOrganizationId } from "#/hooks/useOrganizationId";
import { CurrentUser } from "#/providers/CurrentUser";
import UpdateVersionDialog from "./UpdateVersionDialog";
const documentFragment = graphql`
fragment DocumentActionsDropdown_documentFragment on Document {
id
@@ -43,10 +42,37 @@ const documentFragment = graphql`
canUnarchive: permission(action: "core:document:unarchive")
canDelete: permission(action: "core:document:delete")
versions(first: 20) {
__id
totalCount
}
...UpdateVersionDialogFragment
}
`;
const createDraftDocumentVersionMutation = graphql`
mutation DocumentActionsDropdown_createDraftMutation(
$input: CreateDraftDocumentVersionInput!
$connections: [ID!]!
) {
createDraftDocumentVersion(input: $input) {
documentVersionEdge @prependEdge(connections: $connections) {
node {
id
content
status
publishedAt
major
minor
updatedAt
signatures(first: 100) {
edges {
node {
id
state
}
}
}
}
}
}
}
`;
@@ -118,7 +144,6 @@ export function DocumentActionsDropdownn(props: {
const { versionId } = useParams();
const { __ } = useTranslate();
const { email: defaultEmail } = use(CurrentUser);
const updateDialogRef = useRef<{ open: () => void }>(null);
const pdfDownloadDialogRef = useRef<PdfDownloadDialogRef>(null);
const confirm = useConfirm();
const { toast } = useToast();
@@ -128,6 +153,8 @@ export function DocumentActionsDropdownn(props: {
const isDraft = version.status === "DRAFT";
const [createDraftDocumentVersion, isCreatingDraft]
= useMutation<DocumentActionsDropdown_createDraftMutation>(createDraftDocumentVersionMutation);
const [deleteDocument, isDeleting] = useDeleteDocumentMutation();
const [archiveDocument, isArchiving]
= useMutation<DocumentActionsDropdown_archiveMutation>(archiveDocumentMutation);
@@ -144,6 +171,38 @@ export function DocumentActionsDropdownn(props: {
},
);
const handleCreateDraft = () => {
const connectionId = ConnectionHandler.getConnectionID(document.id, "DocumentversionsDropdownMenu_versions");
createDraftDocumentVersion({
variables: {
input: {
documentID: document.id,
},
connections: [connectionId],
},
onCompleted: (response, errors) => {
if (errors) {
toast({
variant: "error",
title: __("Error creating draft"),
description:
errors[0]?.message || __("An unknown error occurred"),
});
return;
}
const newVersionId
= response.createDraftDocumentVersion.documentVersionEdge.node.id;
void navigate(`/organizations/${organizationId}/documents/${document.id}/versions/${newVersionId}`);
},
onError(error) {
console.log(error);
toast({ title: __("Error"), description: error.message, variant: "error" });
},
});
};
const handleArchive = () => {
confirm(
() =>
@@ -220,16 +279,13 @@ export function DocumentActionsDropdownn(props: {
};
const handleDeleteDraft = () => {
const versionsConnectionId = ConnectionHandler.getConnectionID(
document.id,
"DocumentLayout_versions",
);
const connectionId = ConnectionHandler.getConnectionID(document.id, "DocumentversionsDropdownMenu_versions");
confirm(
() =>
deleteDraftDocumentVersion({
variables: {
input: { documentVersionId: version.id },
connections: [document.versions.__id, versionsConnectionId],
connections: [connectionId],
},
onSuccess() {
if (versionId) {
@@ -285,10 +341,6 @@ export function DocumentActionsDropdownn(props: {
return (
<>
<UpdateVersionDialog
ref={updateDialogRef}
fKey={document}
/>
<PdfDownloadDialog
ref={pdfDownloadDialogRef}
onDownload={options => void handleExportDocumentVersion(options)}
@@ -296,12 +348,13 @@ export function DocumentActionsDropdownn(props: {
defaultEmail={defaultEmail}
/>
<ActionDropdown variant="secondary">
{document.canUpdate && (
{document.canUpdate && !isDraft && (
<DropdownItem
onClick={() => updateDialogRef.current?.open()}
onClick={handleCreateDraft}
icon={IconPencil}
disabled={isCreatingDraft}
>
{isDraft ? __("Edit draft document") : __("Create new draft")}
{__("Create new draft")}
</DropdownItem>
)}
{isDraft

View File

@@ -24,7 +24,7 @@ export const documentVersionsDropdownMenuQuery = graphql`
document: node(id: $documentId) {
__typename
... on Document {
versions(first: 20) {
versions(first: 20) @connection(key: "DocumentversionsDropdownMenu_versions") {
edges {
node {
id

View File

@@ -1,235 +0,0 @@
// Copyright (c) 2026 Probo Inc <hello@getprobo.com>.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
import { useTranslate } from "@probo/i18n";
import {
Breadcrumb,
Button,
Dialog,
DialogContent,
DialogFooter,
Spinner,
Textarea,
useDialogRef,
useToast,
} from "@probo/ui";
import { type RefObject, useEffect } from "react";
import { useFragment, useMutation } from "react-relay";
import { useNavigate } from "react-router";
import { graphql } from "relay-runtime";
import { z } from "zod";
import type { UpdateVersionDialogCreateMutation } from "#/__generated__/core/UpdateVersionDialogCreateMutation.graphql";
import type { UpdateVersionDialogFragment$key } from "#/__generated__/core/UpdateVersionDialogFragment.graphql";
import type { UpdateVersionDialogUpdateMutation } from "#/__generated__/core/UpdateVersionDialogUpdateMutation.graphql";
import { useFormWithSchema } from "#/hooks/useFormWithSchema";
import { useMutationWithToasts } from "#/hooks/useMutationWithToasts";
import { useOrganizationId } from "#/hooks/useOrganizationId";
const fragment = graphql`
fragment UpdateVersionDialogFragment on Document {
id
versions(first: 20) @connection(key: "DocumentLayout_versions") {
__id
edges {
node {
id
status
content
}
}
}
}
`;
const createDraftDocument = graphql`
mutation UpdateVersionDialogCreateMutation(
$input: CreateDraftDocumentVersionInput!
$connections: [ID!]!
) {
createDraftDocumentVersion(input: $input) {
documentVersionEdge @prependEdge(connections: $connections) {
node {
id
content
status
publishedAt
major
minor
updatedAt
signatures(first: 100) {
edges {
node {
id
state
}
}
}
}
}
}
}
`;
const updateDocumentMutation = graphql`
mutation UpdateVersionDialogUpdateMutation(
$input: UpdateDocumentVersionInput!
) {
updateDocumentVersion(input: $input) {
documentVersion {
id
content
}
}
}
`;
type UpdateVersionDialogProps = {
fKey: UpdateVersionDialogFragment$key;
ref: RefObject<{ open: () => void } | null>;
};
const versionSchema = z.object({
content: z.string(),
});
export default function UpdateVersionDialog(props: UpdateVersionDialogProps) {
const { fKey, ref } = props;
const organizationId = useOrganizationId();
const navigate = useNavigate();
const { __ } = useTranslate();
const { toast } = useToast();
const dialogRef = useDialogRef();
const document = useFragment<UpdateVersionDialogFragment$key>(fragment, fKey);
const version = document.versions.edges[0].node;
const isDraft = version.status === "DRAFT";
const [createDraftDocumentVersion, isCreatingDraft]
= useMutation<UpdateVersionDialogCreateMutation>(createDraftDocument);
const [updateDocumentVersion, isUpdating]
= useMutationWithToasts<UpdateVersionDialogUpdateMutation>(
updateDocumentMutation,
{
successMessage: __("Document updated successfully."),
errorMessage: __("Failed to update document"),
},
);
const { handleSubmit, register } = useFormWithSchema(versionSchema, {
defaultValues: {
content: version.content,
},
});
useEffect(() => {
if (!ref.current) {
ref.current = {
open: () => {
dialogRef.current?.open();
},
};
}
});
if (!version) {
return;
}
const onSubmit = async (data: z.infer<typeof versionSchema>) => {
if (isDraft) {
await updateDocumentVersion({
variables: {
input: {
documentVersionId: version.id,
content: data.content,
},
},
onSuccess: () => {
dialogRef.current?.close();
},
});
} else {
createDraftDocumentVersion({
variables: {
input: {
documentID: document.id,
},
connections: [document.versions.__id],
},
onCompleted: (createResponse, errors) => {
if (errors) {
toast({
variant: "error",
title: __("Error creating draft"),
description:
errors[0]?.message || __("An unknown error occurred"),
});
return;
}
const newVersionId
= createResponse?.createDraftDocumentVersion?.documentVersionEdge
?.node?.id;
if (newVersionId && data.content !== version.content) {
void updateDocumentVersion({
variables: {
input: {
documentVersionId: newVersionId,
content: data.content,
},
},
onSuccess: () => {
dialogRef.current?.close();
void navigate(`/organizations/${organizationId}/documents/${document.id}/versions/${newVersionId}`);
},
});
} else {
dialogRef.current?.close();
void navigate(`/organizations/${organizationId}/documents/${document.id}/versions/${newVersionId}`);
}
},
});
}
};
const isLoading = isCreatingDraft || isUpdating;
return (
<Dialog
ref={dialogRef}
title={<Breadcrumb items={[__("Documents"), __("Edit document")]} />}
>
<form onSubmit={e => void handleSubmit(onSubmit)(e)}>
<DialogContent>
<Textarea
id="content"
variant="ghost"
autogrow
required
placeholder={__("Add content")}
aria-label={__("Content")}
className="p-6"
{...register("content")}
/>
</DialogContent>
<DialogFooter>
<Button disabled={isLoading} type="submit">
{isLoading && <Spinner />}
{__("Update document")}
</Button>
</DialogFooter>
</form>
</Dialog>
);
}