From 7be893f9a31b1145bf11ea333828d891a0471e01 Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Fri, 20 Feb 2026 18:53:00 -0500 Subject: [PATCH] Refactor templates (#11027) closes #8674 --- app/actions/definitions/collections.tsx | 12 +- app/actions/definitions/documents.tsx | 104 +--- app/actions/definitions/templates.tsx | 224 ++++++++ app/actions/sections.ts | 9 + .../CommandBar/useTemplatesAction.tsx | 10 +- app/components/DocumentBreadcrumb.tsx | 11 +- .../DocumentExplorer/Components.tsx | 17 + .../{ => DocumentExplorer}/DocumentCopy.tsx | 69 ++- .../DocumentExplorer.tsx | 25 +- .../DocumentExplorerNode.tsx | 0 .../DocumentExplorerSearchResult.tsx | 2 +- .../DocumentExplorer}/DocumentMove.tsx | 44 +- .../DocumentExplorer/TemplateMove.tsx | 87 ++++ app/components/DocumentExplorer/index.ts | 3 + app/components/DocumentListItem.tsx | 7 +- app/components/DocumentMeta.tsx | 3 +- app/components/PaginatedDocumentList.tsx | 1 - app/components/Table.tsx | 17 +- app/components/Template/TemplateEdit.tsx | 30 ++ app/components/Template/TemplateForm.tsx | 103 ++++ app/components/Template/TemplateNew.tsx | 36 ++ app/components/TemplatizeDialog/index.tsx | 11 +- app/hooks/useDocumentMenuAction.tsx | 8 +- app/hooks/useTemplateMenuActions.tsx | 21 +- app/hooks/useTemplateSettingsActions.tsx | 60 +++ app/menus/DocumentMenu.tsx | 3 +- app/menus/NewTemplateMenu.tsx | 3 +- app/menus/TemplateMenu.tsx | 32 ++ app/menus/TemplatesMenu.tsx | 3 +- app/models/Document.ts | 33 +- app/models/Template.ts | 155 ++++++ app/models/helpers/ProsemirrorHelper.ts | 10 +- app/routes/settings.tsx | 19 +- app/scenes/Document/components/DataLoader.tsx | 2 +- app/scenes/Document/components/Document.tsx | 17 +- .../Document/components/DocumentMeta.tsx | 18 +- app/scenes/Document/components/Editor.tsx | 15 +- app/scenes/Document/components/Header.tsx | 54 +- app/scenes/Document/components/Notices.tsx | 31 +- app/scenes/DocumentDelete.tsx | 30 +- app/scenes/DocumentNew.tsx | 10 +- app/scenes/Search/Search.tsx | 1 - app/scenes/Settings/Template.tsx | 125 +++++ app/scenes/Settings/TemplateNew.tsx | 89 ++++ app/scenes/Settings/Templates.tsx | 169 ++++-- .../Settings/components/EmojisTable.tsx | 2 +- .../Settings/components/MembersTable.tsx | 2 +- .../Settings/components/TemplatesTable.tsx | 180 +++++++ app/stores/DocumentsStore.ts | 73 +-- app/stores/RootStore.ts | 3 + app/stores/TemplatesStore.ts | 82 +++ app/stores/UiStore.ts | 2 +- .../server/tasks/DeliverWebhookTask.ts | 4 + server/commands/documentCreator.test.ts | 40 +- server/commands/documentCreator.ts | 38 +- server/commands/documentDuplicator.ts | 3 +- server/commands/documentMover.ts | 279 +++++----- server/commands/documentUpdater.ts | 2 +- server/models/Document.test.ts | 13 - server/models/Document.ts | 32 +- server/models/Template.ts | 290 +++++++++++ server/models/helpers/DocumentHelper.tsx | 5 +- server/models/index.ts | 2 + server/policies/document.ts | 59 +-- server/policies/index.ts | 1 + server/policies/template.ts | 67 +++ server/presenters/document.ts | 1 - server/presenters/index.ts | 2 + server/presenters/template.ts | 24 + .../queues/processors/WebsocketsProcessor.ts | 59 ++- .../tasks/DetachDraftsFromCollectionTask.ts | 1 - server/queues/tasks/ExportJSONTask.ts | 1 - server/routes/api/documents/documents.test.ts | 247 +-------- server/routes/api/documents/documents.ts | 55 +- server/routes/api/documents/schema.ts | 6 - server/routes/api/index.ts | 2 + server/routes/api/templates/index.ts | 1 + server/routes/api/templates/schema.ts | 94 ++++ server/routes/api/templates/templates.test.ts | 481 ++++++++++++++++++ server/routes/api/templates/templates.ts | 313 ++++++++++++ server/test/factories.ts | 47 ++ server/types.ts | 12 +- shared/i18n/locales/en_US/translation.json | 53 +- 83 files changed, 3185 insertions(+), 1126 deletions(-) create mode 100644 app/actions/definitions/templates.tsx create mode 100644 app/components/DocumentExplorer/Components.tsx rename app/components/{ => DocumentExplorer}/DocumentCopy.tsx (69%) rename app/components/{ => DocumentExplorer}/DocumentExplorer.tsx (95%) rename app/components/{ => DocumentExplorer}/DocumentExplorerNode.tsx (100%) rename app/components/{ => DocumentExplorer}/DocumentExplorerSearchResult.tsx (96%) rename app/{scenes => components/DocumentExplorer}/DocumentMove.tsx (74%) create mode 100644 app/components/DocumentExplorer/TemplateMove.tsx create mode 100644 app/components/DocumentExplorer/index.ts create mode 100644 app/components/Template/TemplateEdit.tsx create mode 100644 app/components/Template/TemplateForm.tsx create mode 100644 app/components/Template/TemplateNew.tsx create mode 100644 app/hooks/useTemplateSettingsActions.tsx create mode 100644 app/menus/TemplateMenu.tsx create mode 100644 app/models/Template.ts create mode 100644 app/scenes/Settings/Template.tsx create mode 100644 app/scenes/Settings/TemplateNew.tsx create mode 100644 app/scenes/Settings/components/TemplatesTable.tsx create mode 100644 app/stores/TemplatesStore.ts create mode 100644 server/models/Template.ts create mode 100644 server/policies/template.ts create mode 100644 server/presenters/template.ts create mode 100644 server/routes/api/templates/index.ts create mode 100644 server/routes/api/templates/schema.ts create mode 100644 server/routes/api/templates/templates.test.ts create mode 100644 server/routes/api/templates/templates.ts diff --git a/app/actions/definitions/collections.tsx b/app/actions/definitions/collections.tsx index 1bee6bb9f5..6107f73440 100644 --- a/app/actions/definitions/collections.tsx +++ b/app/actions/definitions/collections.tsx @@ -29,8 +29,8 @@ import DynamicCollectionIcon from "~/components/Icons/CollectionIcon"; import { getHeaderExpandedKey } from "~/components/Sidebar/components/Header"; import { createAction, - createActionWithChildren, createInternalLinkAction, + createActionWithChildren, } from "~/actions"; import { ActiveCollectionSection, CollectionSection } from "~/actions/sections"; import { setPersistedState } from "~/hooks/usePersistedState"; @@ -530,15 +530,9 @@ export const createTemplate = createInternalLinkAction({ getActivePolicies(Collection).some( (policy) => policy.abilities.createDocument ), - to: ({ getActiveModel, sidebarContext }) => { + to: ({ getActiveModel }) => { const collection = getActiveModel(Collection); - const [pathname, search] = newTemplatePath(collection?.id).split("?"); - - return { - pathname, - search, - state: { sidebarContext }, - }; + return newTemplatePath(collection?.id); }, }); diff --git a/app/actions/definitions/documents.tsx b/app/actions/definitions/documents.tsx index 8103100fe9..d5c81327d3 100644 --- a/app/actions/definitions/documents.tsx +++ b/app/actions/definitions/documents.tsx @@ -42,12 +42,11 @@ import { Week } from "@shared/utils/time"; import type UserMembership from "~/models/UserMembership"; import { client } from "~/utils/ApiClient"; import DocumentDelete from "~/scenes/DocumentDelete"; -import DocumentMove from "~/scenes/DocumentMove"; import DocumentPermanentDelete from "~/scenes/DocumentPermanentDelete"; import DocumentPublish from "~/scenes/DocumentPublish"; import DeleteDocumentsInTrash from "~/scenes/Trash/components/DeleteDocumentsInTrash"; import ConfirmationDialog from "~/components/ConfirmationDialog"; -import DocumentCopy from "~/components/DocumentCopy"; +import DocumentCopy from "~/components/DocumentExplorer/DocumentCopy"; import { DocumentDownload } from "~/components/DocumentDownload"; import MarkdownIcon from "~/components/Icons/MarkdownIcon"; import { getHeaderExpandedKey } from "~/components/Sidebar/components/Header"; @@ -87,6 +86,7 @@ import type { } from "~/types"; import lazyWithRetry from "~/utils/lazyWithRetry"; import env from "~/env"; +import DocumentMove from "~/components/DocumentExplorer/DocumentMove"; const Insights = lazyWithRetry( () => import("~/scenes/Document/components/Insights") @@ -138,18 +138,13 @@ export const editDocument = createInternalLinkAction({ keywords: "edit", icon: , visible: ({ activeDocumentId, stores }) => { - const { auth, documents, policies } = stores; + const { auth, policies } = stores; - const document = activeDocumentId - ? documents.get(activeDocumentId) - : undefined; const can = activeDocumentId ? policies.abilities(activeDocumentId) : undefined; - return ( - !!can?.update && !!auth.user?.separateEditMode && !document?.template - ); + return !!can?.update && !!auth.user?.separateEditMode; }, to: ({ activeDocumentId, stores }) => { const document = activeDocumentId @@ -222,12 +217,7 @@ export const createDocumentFromTemplate = createInternalLinkAction({ ? stores.documents.get(activeDocumentId) : undefined; - if ( - !currentTeamId || - !document?.isTemplate || - !!document?.isDraft || - !!document?.isDeleted - ) { + if (!currentTeamId || !!document?.isDraft || !!document?.isDeleted) { return false; } @@ -468,7 +458,7 @@ export const publishDocument = createAction({ return; } - if (document?.collectionId || document?.template) { + if (document?.collectionId) { await document.save(undefined, { publish: true, }); @@ -1055,7 +1045,7 @@ export const createTemplateFromDocument = createAction({ const document = activeDocumentId ? stores.documents.get(activeDocumentId) : undefined; - if (document?.isTemplate || !document?.isActive) { + if (!document?.isActive) { return false; } return !!( @@ -1107,46 +1097,8 @@ export const searchDocumentsForQuery = (query: string) => visible: ({ location }) => location.pathname !== searchPath(), }); -export const moveTemplateToWorkspace = createAction({ - name: ({ t }) => t("Move to workspace"), - analyticsName: "Move template to workspace", - section: DocumentSection, - icon: , - iconInContextMenu: false, - visible: ({ activeDocumentId, stores }) => { - if (!activeDocumentId) { - return false; - } - const document = stores.documents.get(activeDocumentId); - if (!document || !document.template || document.isWorkspaceTemplate) { - return false; - } - return !!stores.policies.abilities(activeDocumentId).move; - }, - perform: async ({ activeDocumentId, stores }) => { - if (activeDocumentId) { - const document = stores.documents.get(activeDocumentId); - if (!document) { - return; - } - - await document.move({ - collectionId: null, - }); - } - }, -}); - export const moveDocumentToCollection = createAction({ - name: ({ activeDocumentId, stores, t }) => { - if (!activeDocumentId) { - return t("Move"); - } - const document = stores.documents.get(activeDocumentId); - return document?.template && document?.collectionId - ? t("Move to collection") - : t("Move"); - }, + name: ({ t }) => t("Move"), analyticsName: "Move document", section: ActiveDocumentSection, icon: , @@ -1184,8 +1136,7 @@ export const moveDocument = createAction({ return false; } const document = stores.documents.get(activeDocumentId); - // Don't show the button if this is a non-workspace template. - if (!document || (document.template && !document.isWorkspaceTemplate)) { + if (!document) { return false; } return !!stores.policies.abilities(activeDocumentId).move; @@ -1193,25 +1144,6 @@ export const moveDocument = createAction({ perform: moveDocumentToCollection.perform, }); -export const moveTemplate = createActionWithChildren({ - name: ({ t }) => t("Move"), - analyticsName: "Move document", - section: ActiveDocumentSection, - icon: , - visible: ({ activeDocumentId, stores }) => { - if (!activeDocumentId) { - return false; - } - const document = stores.documents.get(activeDocumentId); - // Don't show the menu if this is not a template (or) a workspace template. - if (!document || !document.template || document.isWorkspaceTemplate) { - return false; - } - return !!stores.policies.abilities(activeDocumentId).move; - }, - children: [moveTemplateToWorkspace, moveDocumentToCollection], -}); - export const archiveDocument = createAction({ name: ({ t }) => `${t("Archive")}…`, analyticsName: "Archive document", @@ -1270,10 +1202,7 @@ export const restoreDocument = createAction({ : undefined; const can = stores.policies.abilities(document.id); - return ( - !!(document.isWorkspaceTemplate || collection?.isActive) && - !!(can.restore || can.unarchive) - ); + return !!collection?.isActive && !!(can.restore || can.unarchive); }, perform: async ({ t, stores, activeDocumentId }) => { const document = activeDocumentId @@ -1310,10 +1239,7 @@ export const restoreDocumentToCollection = createActionWithChildren({ ? stores.collections.get(document.collectionId) : undefined; - return ( - !(document.isWorkspaceTemplate || collection?.isActive) && - !!(can.restore || can.unarchive) - ); + return !collection?.isActive && !!(can.restore || can.unarchive); }, children: ({ t, activeDocumentId, stores }) => { const { collections, documents, policies } = stores; @@ -1498,12 +1424,7 @@ export const openDocumentInsights = createAction({ ? stores.documents.get(activeDocumentId) : undefined; - return ( - !!activeDocumentId && - can.listViews && - !document?.isTemplate && - !document?.isDeleted - ); + return !!activeDocumentId && can.listViews && !document?.isDeleted; }, perform: ({ activeDocumentId, stores, t }) => { const document = activeDocumentId @@ -1604,7 +1525,6 @@ export const rootDocumentActions = [ searchInDocument, duplicateDocument, leaveDocument, - moveTemplateToWorkspace, moveDocumentToCollection, openRandomDocument, permanentlyDeleteDocument, diff --git a/app/actions/definitions/templates.tsx b/app/actions/definitions/templates.tsx new file mode 100644 index 0000000000..47eeabd993 --- /dev/null +++ b/app/actions/definitions/templates.tsx @@ -0,0 +1,224 @@ +import copy from "copy-to-clipboard"; +import { + CaseSensitiveIcon, + CollectionIcon, + CopyIcon, + MoveIcon, + NewDocumentIcon, + PlusIcon, + PrintIcon, + TrashIcon, +} from "outline-icons"; +import { Trans } from "react-i18next"; +import { toast } from "sonner"; +import ConfirmationDialog from "~/components/ConfirmationDialog"; +import TemplateMove from "~/components/DocumentExplorer/TemplateMove"; +import { + createAction, + createActionWithChildren, + createInternalLinkAction, +} from "~/actions"; +import { newDocumentPath, newTemplatePath, urlify } from "~/utils/routeHelpers"; +import { ActiveTemplateSection, TemplateSection } from "../sections"; +import Template from "~/models/Template"; +import { AvatarSize } from "~/components/Avatar"; +import TeamLogo from "~/components/TeamLogo"; + +export const createTemplate = createInternalLinkAction({ + name: ({ t }) => t("New template"), + analyticsName: "New template", + section: TemplateSection, + icon: , + keywords: "new create template", + visible: ({ currentTeamId, stores }) => + !!stores.policies.abilities(currentTeamId!).createTemplate, + to: newTemplatePath(), +}); + +export const deleteTemplate = createAction({ + name: ({ t }) => `${t("Delete")}…`, + analyticsName: "Delete template", + section: ActiveTemplateSection, + icon: , + dangerous: true, + visible: ({ getActivePolicies }) => + getActivePolicies(Template).some((policy) => policy.abilities.delete), + perform: ({ getActiveModel, stores, t }) => { + const template = getActiveModel(Template); + if (!template) { + return; + } + + stores.dialogs.openModal({ + title: t("Delete {{ documentName }}", { + documentName: t("template"), + }), + content: ( + { + await template.delete(); + toast.success(t("Template deleted")); + }} + savingText={`${t("Deleting")}…`} + danger + > + , + }} + /> + + ), + }); + }, +}); + +export const moveTemplateToWorkspace = createAction({ + name: ({ t }) => t("Move to workspace"), + analyticsName: "Move template to workspace", + section: ActiveTemplateSection, + icon: ({ stores }) => { + const { team } = stores.auth; + return ; + }, + visible: ({ getActiveModel }) => { + const template = getActiveModel(Template); + return !!template?.collectionId; + }, + perform: async ({ getActiveModel, stores, t }) => { + const template = getActiveModel(Template); + if (!template) { + return; + } + + try { + await template.save({ collectionId: null }); + toast.success(t("Template moved")); + stores.dialogs.closeAllModals(); + } catch (_err) { + toast.error(t("Couldn't move the template, try again?")); + } + }, +}); + +export const moveTemplateToCollection = createAction({ + name: ({ t }) => t("Move to collection"), + analyticsName: "Move template to collection", + section: ActiveTemplateSection, + icon: , + perform: ({ getActiveModel, stores, t }) => { + const template = getActiveModel(Template); + if (!template) { + return; + } + + stores.dialogs.openModal({ + title: t("Move template"), + content: , + }); + }, +}); + +export const moveTemplate = createActionWithChildren({ + name: ({ t }) => t("Move"), + analyticsName: "Move template", + section: ActiveTemplateSection, + icon: , + visible: ({ getActivePolicies }) => + getActivePolicies(Template).some((policy) => policy.abilities.move), + children: [moveTemplateToWorkspace, moveTemplateToCollection], +}); + +export const createDocumentFromTemplate = createInternalLinkAction({ + name: ({ t }) => t("New document"), + analyticsName: "New document from template", + section: ActiveTemplateSection, + icon: , + keywords: "create", + visible: ({ currentTeamId, getActiveModel, stores }) => { + const template = getActiveModel(Template); + if (!template || !currentTeamId) { + return false; + } + + if (template.collectionId) { + return !!stores.policies.abilities(template.collectionId).createDocument; + } + return !!stores.policies.abilities(currentTeamId).createDocument; + }, + to: ({ getActiveModel, activeCollectionId, sidebarContext }) => { + const template = getActiveModel(Template); + if (!template) { + return ""; + } + const collectionId = template?.collectionId ?? activeCollectionId; + + const [pathname, search] = newDocumentPath(collectionId, { + templateId: template.id, + }).split("?"); + + return { + pathname, + search, + state: { sidebarContext }, + }; + }, +}); + +export const copyTemplateLink = createAction({ + name: ({ t }) => t("Copy link"), + analyticsName: "Copy template link", + section: ActiveTemplateSection, + icon: , + iconInContextMenu: false, + perform: ({ getActiveModel, t }) => { + const template = getActiveModel(Template); + if (template) { + copy(urlify(template.path)); + toast.success(t("Link copied to clipboard")); + } + }, +}); + +export const copyTemplateAsPlainText = createAction({ + name: ({ t }) => t("Copy as text"), + analyticsName: "Copy template as text", + section: ActiveTemplateSection, + icon: , + iconInContextMenu: false, + perform: async ({ getActiveModel, t }) => { + const template = getActiveModel(Template); + if (template) { + const { ProsemirrorHelper } = + await import("~/models/helpers/ProsemirrorHelper"); + copy(ProsemirrorHelper.toPlainText(template)); + toast.success(t("Text copied to clipboard")); + } + }, +}); + +export const copyTemplate = createActionWithChildren({ + name: ({ t }) => t("Copy"), + analyticsName: "Copy template", + section: ActiveTemplateSection, + icon: , + keywords: "clipboard", + children: [copyTemplateLink, copyTemplateAsPlainText], +}); + +export const printTemplate = createAction({ + name: ({ t, isMenu }) => (isMenu ? t("Print") : t("Print template")), + analyticsName: "Print template", + section: ActiveTemplateSection, + icon: , + visible: ({ getActiveModel }) => !!getActiveModel(Template) && !!window.print, + perform: () => { + queueMicrotask(window.print); + }, +}); + +export const rootTemplateActions = [moveTemplate, createDocumentFromTemplate]; diff --git a/app/actions/sections.ts b/app/actions/sections.ts index df2ddd9139..ef55a448c7 100644 --- a/app/actions/sections.ts +++ b/app/actions/sections.ts @@ -24,6 +24,15 @@ export const ActiveDocumentSection = ({ t, stores }: ActionContext) => { ActiveDocumentSection.priority = 0.9; +export const TemplateSection = ({ t }: ActionContext) => t("Template"); + +export const ActiveTemplateSection = ({ t, stores }: ActionContext) => { + const activeTemplate = stores.templates.active; + return `${t("Template")} · ${activeTemplate?.titleWithDefault}`; +}; + +ActiveTemplateSection.priority = 0.9; + export const RecentSection = ({ t }: ActionContext) => t("Recently viewed"); RecentSection.priority = 1; diff --git a/app/components/CommandBar/useTemplatesAction.tsx b/app/components/CommandBar/useTemplatesAction.tsx index faed008fea..0d82c7a6c7 100644 --- a/app/components/CommandBar/useTemplatesAction.tsx +++ b/app/components/CommandBar/useTemplatesAction.tsx @@ -11,15 +11,15 @@ import useStores from "~/hooks/useStores"; import { newDocumentPath } from "~/utils/routeHelpers"; const useTemplatesAction = () => { - const { documents } = useStores(); + const { templates } = useStores(); useEffect(() => { - void documents.fetchAllTemplates(); - }, [documents]); + void templates.fetchAll(); + }, [templates]); const actions = useMemo( () => - documents.templatesAlphabetical.map((template) => + templates.alphabetical.map((template) => createInternalLinkAction({ name: template.titleWithDefault, analyticsName: "New document", @@ -66,7 +66,7 @@ const useTemplatesAction = () => { }, }) ), - [documents.templatesAlphabetical] + [templates.alphabetical] ); const newFromTemplate = useMemo( diff --git a/app/components/DocumentBreadcrumb.tsx b/app/components/DocumentBreadcrumb.tsx index df584d0868..62521b8349 100644 --- a/app/components/DocumentBreadcrumb.tsx +++ b/app/components/DocumentBreadcrumb.tsx @@ -1,5 +1,5 @@ import { observer } from "mobx-react"; -import { ArchiveIcon, GoToIcon, ShapesIcon, TrashIcon } from "outline-icons"; +import { ArchiveIcon, GoToIcon, TrashIcon } from "outline-icons"; import * as React from "react"; import { useTranslation } from "react-i18next"; import styled from "styled-components"; @@ -11,7 +11,7 @@ import CollectionIcon from "~/components/Icons/CollectionIcon"; import { useLocationSidebarContext } from "~/hooks/useLocationSidebarContext"; import usePolicy from "~/hooks/usePolicy"; import useStores from "~/hooks/useStores"; -import { archivePath, settingsPath, trashPath } from "~/utils/routeHelpers"; +import { archivePath, trashPath } from "~/utils/routeHelpers"; import { createInternalLinkAction } from "~/actions"; import { ActiveDocumentSection } from "~/actions/sections"; @@ -67,13 +67,6 @@ function DocumentBreadcrumb( visible: document.isArchived, to: archivePath(), }), - createInternalLinkAction({ - name: t("Templates"), - section: ActiveDocumentSection, - icon: , - visible: document.template, - to: settingsPath("templates"), - }), createInternalLinkAction({ name: collection?.name, section: ActiveDocumentSection, diff --git a/app/components/DocumentExplorer/Components.tsx b/app/components/DocumentExplorer/Components.tsx new file mode 100644 index 0000000000..41cf330de6 --- /dev/null +++ b/app/components/DocumentExplorer/Components.tsx @@ -0,0 +1,17 @@ +import styled from "styled-components"; +import Flex from "../Flex"; + +export const FlexContainer = styled(Flex)` + margin-left: -24px; + margin-right: -24px; + margin-bottom: -24px; + outline: none; +`; + +export const Footer = styled(Flex)` + height: 64px; + border-top: 1px solid ${(props) => props.theme.horizontalRule}; + padding-left: 24px; + padding-right: 24px; + flex-shrink: 0; +`; diff --git a/app/components/DocumentCopy.tsx b/app/components/DocumentExplorer/DocumentCopy.tsx similarity index 69% rename from app/components/DocumentCopy.tsx rename to app/components/DocumentExplorer/DocumentCopy.tsx index dec189d0b1..0210a6a078 100644 --- a/app/components/DocumentCopy.tsx +++ b/app/components/DocumentExplorer/DocumentCopy.tsx @@ -5,13 +5,13 @@ import { toast } from "sonner"; import styled from "styled-components"; import type { NavigationNode } from "@shared/types"; import type Document from "~/models/Document"; -import { FlexContainer, Footer, StyledText } from "~/scenes/DocumentMove"; import Button from "~/components/Button"; -import DocumentExplorer from "~/components/DocumentExplorer"; +import Switch from "~/components/Switch"; +import Text from "~/components/Text"; import useCollectionTrees from "~/hooks/useCollectionTrees"; import useStores from "~/hooks/useStores"; -import Switch from "./Switch"; -import Text from "./Text"; +import { FlexContainer, Footer } from "./Components"; +import DocumentExplorer from "./DocumentExplorer"; type Props = { /** The original document to duplicate */ @@ -37,13 +37,8 @@ function DocumentCopy({ document, onSubmit }: Props) { : true ); - if (document.isTemplate) { - return nodes - .filter((node) => node.type === "collection") - .map((node) => ({ ...node, children: [] })); - } return nodes; - }, [policies, collectionTrees, document.isTemplate]); + }, [policies, collectionTrees]); const copy = async () => { if (!selectedPath) { @@ -80,34 +75,32 @@ function DocumentCopy({ document, onSubmit }: Props) { onSelect={selectPath} defaultValue={document.parentDocumentId || document.collectionId || ""} /> - {!document.isTemplate && ( - - {document.collectionId && ( - - - - )} - {document.publishedAt && document.childDocuments.length > 0 && ( - - - - )} - - )} + + {document.collectionId && ( + + + + )} + {document.publishedAt && document.childDocuments.length > 0 && ( + + + + )} +