feat: duplicate a document as draft (#6782)

* feat: duplicate a document as draft

* review

* Default `publish` toggle to match current document

Ensures duplicating a draft does not publish it.

---------

Co-authored-by: Tom Moor <tom.moor@gmail.com>
This commit is contained in:
Hemachandar
2024-04-13 06:17:59 +05:30
committed by GitHub
parent 0b99a88cd7
commit 581944e754
5 changed files with 63 additions and 11 deletions

View File

@@ -19,9 +19,17 @@ function DuplicateDialog({ document, onSubmit }: Props) {
const defaultTitle = t(`Copy of {{ documentName }}`, { const defaultTitle = t(`Copy of {{ documentName }}`, {
documentName: document.title, documentName: document.title,
}); });
const [publish, setPublish] = React.useState<boolean>(!!document.publishedAt);
const [recursive, setRecursive] = React.useState<boolean>(true); const [recursive, setRecursive] = React.useState<boolean>(true);
const [title, setTitle] = React.useState<string>(defaultTitle); const [title, setTitle] = React.useState<string>(defaultTitle);
const handlePublishChange = React.useCallback(
(ev: React.ChangeEvent<HTMLInputElement>) => {
setPublish(ev.target.checked);
},
[]
);
const handleRecursiveChange = React.useCallback( const handleRecursiveChange = React.useCallback(
(ev: React.ChangeEvent<HTMLInputElement>) => { (ev: React.ChangeEvent<HTMLInputElement>) => {
setRecursive(ev.target.checked); setRecursive(ev.target.checked);
@@ -38,6 +46,7 @@ function DuplicateDialog({ document, onSubmit }: Props) {
const handleSubmit = async () => { const handleSubmit = async () => {
const result = await document.duplicate({ const result = await document.duplicate({
publish,
recursive, recursive,
title, title,
}); });
@@ -56,6 +65,16 @@ function DuplicateDialog({ document, onSubmit }: Props) {
defaultValue={defaultTitle} defaultValue={defaultTitle}
/> />
{document.publishedAt && !document.isTemplate && ( {document.publishedAt && !document.isTemplate && (
<>
<Text size="small">
<Switch
name="publish"
label={t("Published")}
labelPosition="right"
checked={publish}
onChange={handlePublishChange}
/>
</Text>
<Text size="small"> <Text size="small">
<Switch <Switch
name="recursive" name="recursive"
@@ -65,6 +84,7 @@ function DuplicateDialog({ document, onSubmit }: Props) {
onChange={handleRecursiveChange} onChange={handleRecursiveChange}
/> />
</Text> </Text>
</>
)} )}
</ConfirmationDialog> </ConfirmationDialog>
); );

View File

@@ -509,8 +509,11 @@ export default class Document extends ParanoidModel {
move = (collectionId: string, parentDocumentId?: string | undefined) => move = (collectionId: string, parentDocumentId?: string | undefined) =>
this.store.move(this.id, collectionId, parentDocumentId); this.store.move(this.id, collectionId, parentDocumentId);
duplicate = (options?: { title?: string; recursive?: boolean }) => duplicate = (options?: {
this.store.duplicate(this, options); title?: string;
publish?: boolean;
recursive?: boolean;
}) => this.store.duplicate(this, options);
@computed @computed
get pinned(): boolean { get pinned(): boolean {

View File

@@ -567,6 +567,7 @@ export default class DocumentsStore extends Store<Document> {
document: Document, document: Document,
options?: { options?: {
title?: string; title?: string;
publish?: boolean;
recursive?: boolean; recursive?: boolean;
} }
): Promise<Document[]> => { ): Promise<Document[]> => {

View File

@@ -26,6 +26,7 @@ describe("documentDuplicator", () => {
expect(response[0].title).toEqual(original.title); expect(response[0].title).toEqual(original.title);
expect(response[0].text).toEqual(original.text); expect(response[0].text).toEqual(original.text);
expect(response[0].emoji).toEqual(original.emoji); expect(response[0].emoji).toEqual(original.emoji);
expect(response[0].publishedAt).toBeInstanceOf(Date);
}); });
it("should duplicate document with title override", async () => { it("should duplicate document with title override", async () => {
@@ -51,6 +52,7 @@ describe("documentDuplicator", () => {
expect(response[0].title).toEqual("New title"); expect(response[0].title).toEqual("New title");
expect(response[0].text).toEqual(original.text); expect(response[0].text).toEqual(original.text);
expect(response[0].emoji).toEqual(original.emoji); expect(response[0].emoji).toEqual(original.emoji);
expect(response[0].publishedAt).toBeInstanceOf(Date);
}); });
it("should duplicate child documents with recursive=true", async () => { it("should duplicate child documents with recursive=true", async () => {
@@ -81,4 +83,29 @@ describe("documentDuplicator", () => {
expect(response).toHaveLength(2); expect(response).toHaveLength(2);
}); });
it("should duplicate existing document as draft", async () => {
const user = await buildUser();
const original = await buildDocument({
userId: user.id,
teamId: user.teamId,
});
const response = await sequelize.transaction((transaction) =>
documentDuplicator({
document: original,
collection: original.collection,
transaction,
publish: false,
user,
ip,
})
);
expect(response).toHaveLength(1);
expect(response[0].title).toEqual(original.title);
expect(response[0].text).toEqual(original.text);
expect(response[0].emoji).toEqual(original.emoji);
expect(response[0].publishedAt).toBeNull();
});
}); });

View File

@@ -197,6 +197,7 @@
"Viewed {{ timeAgo }}": "Viewed {{ timeAgo }}", "Viewed {{ timeAgo }}": "Viewed {{ timeAgo }}",
"Copy of {{ documentName }}": "Copy of {{ documentName }}", "Copy of {{ documentName }}": "Copy of {{ documentName }}",
"Title": "Title", "Title": "Title",
"Published": "Published",
"Include nested documents": "Include nested documents", "Include nested documents": "Include nested documents",
"Emoji Picker": "Emoji Picker", "Emoji Picker": "Emoji Picker",
"Remove": "Remove", "Remove": "Remove",