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:
@@ -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>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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[]> => {
|
||||||
|
|||||||
@@ -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();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
Reference in New Issue
Block a user