diff --git a/CHANGELOG.md b/CHANGELOG.md index 08aa34d88..1771d6c72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ and this project adheres to - ♿️(frontend) use semantic `
` structure in document info card #2379 - 🚸(frontend) print from document options menu #2550 +- 🚚(frontend) add doc move to doc options #2555 ### Fixed diff --git a/src/frontend/apps/impress/src/assets/icons/ui-kit/doc-move-out.svg b/src/frontend/apps/impress/src/assets/icons/ui-kit/doc-move-out.svg index 2a2055079..3f23292b2 100644 --- a/src/frontend/apps/impress/src/assets/icons/ui-kit/doc-move-out.svg +++ b/src/frontend/apps/impress/src/assets/icons/ui-kit/doc-move-out.svg @@ -1,6 +1,18 @@ - - - - - + + + + + diff --git a/src/frontend/apps/impress/src/features/docs/doc-management/api/useMoveDoc.tsx b/src/frontend/apps/impress/src/features/docs/doc-management/api/useMoveDoc.tsx index c0e732259..3a2c2d412 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-management/api/useMoveDoc.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-management/api/useMoveDoc.tsx @@ -2,7 +2,9 @@ import { TreeViewMoveModeEnum } from '@gouvfr-lasuite/ui-kit'; import { useMutation, useQueryClient } from '@tanstack/react-query'; import { APIError, errorCauses, fetchAPI } from '@/api'; +import { KEY_DOC_TREE } from '@/docs/doc-tree/api/useDocTree'; +import { KEY_DOC } from './useDoc'; import { KEY_LIST_DOC } from './useDocs'; export type MoveDocParam = { @@ -37,9 +39,9 @@ export function useMoveDoc() { return useMutation({ mutationFn: moveDoc, onSuccess() { - void queryClient.invalidateQueries({ - queryKey: [KEY_LIST_DOC], - }); + void queryClient.invalidateQueries({ queryKey: [KEY_LIST_DOC] }); + void queryClient.invalidateQueries({ queryKey: [KEY_DOC] }); + void queryClient.invalidateQueries({ queryKey: [KEY_DOC_TREE] }); }, }); } diff --git a/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocMoveModal.tsx b/src/frontend/apps/impress/src/features/docs/doc-management/components/DocMoveModal.tsx similarity index 98% rename from src/frontend/apps/impress/src/features/docs/docs-grid/components/DocMoveModal.tsx rename to src/frontend/apps/impress/src/features/docs/doc-management/components/DocMoveModal.tsx index f43aff36f..ab57f46dc 100644 --- a/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocMoveModal.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-management/components/DocMoveModal.tsx @@ -17,10 +17,12 @@ import { Box, ButtonCloseModal, Text } from '@/components'; import { QuickSearch } from '@/components/quick-search'; import { Doc, useMoveDoc, useTrans } from '@/docs/doc-management'; import { DocSearchContent } from '@/docs/doc-search'; +import { + DocsGridItemDate, + DocsGridItemTitle, +} from '@/docs/docs-grid/components/DocsGridItem'; import { useResponsiveStore } from '@/stores'; -import { DocsGridItemDate, DocsGridItemTitle } from './DocsGridItem'; - const AlertModalRequestAccess = dynamic( () => import('@/docs/doc-share/components/AlertModalRequestAccess').then( @@ -83,12 +85,14 @@ type DocMoveModalGlobalProps = { doc: Doc; isOpen: boolean; onClose: () => void; + onAfterMove?: () => void; }; export const DocMoveModal = ({ doc, isOpen, onClose, + onAfterMove, }: DocMoveModalGlobalProps) => { const { t } = useTranslation(); const [loading, setLoading] = useState(false); @@ -120,10 +124,8 @@ export const DocMoveModal = ({ position: TreeViewMoveModeEnum.FIRST_CHILD, }) .then(() => { - toast( - t(`The document has been moved successfully.`), - VariantType.SUCCESS, - ); + onClose(); + onAfterMove?.(); }) .catch(() => { toast( diff --git a/src/frontend/apps/impress/src/features/docs/doc-management/components/DocToolBox.tsx b/src/frontend/apps/impress/src/features/docs/doc-management/components/DocToolBox.tsx index 9546ae400..0796a68de 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-management/components/DocToolBox.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-management/components/DocToolBox.tsx @@ -21,11 +21,15 @@ import MoreSVG from '@/assets/icons/ui-kit/more_horiz.svg'; import PrintIcon from '@/assets/icons/ui-kit/print.svg'; import SharedIcon from '@/assets/icons/ui-kit/shared.svg'; import DeleteIcon from '@/assets/icons/ui-kit/trash.svg'; +import { printDocumentWithStyles } from '@/docs/doc-export/utils_print'; +import { DocMoveModal } from '@/docs/doc-management/components/DocMoveModal'; import { usePresenterStore } from '@/docs/doc-presenter/stores'; +import { useDetachDoc } from '@/docs/doc-tree/api/useDetach'; import { useAuth } from '@/features/auth'; +import DocMoveInIcon from '@/icons/doc-move-in.svg'; +import DocMoveOutIcon from '@/icons/doc-move-out.svg'; import { useFocusStore, useResponsiveStore } from '@/stores'; -import { printDocumentWithStyles } from '../../doc-export/utils_print'; import { KEY_DOC, KEY_LIST_DOC, @@ -35,7 +39,7 @@ import { useDuplicateDoc, } from '../api'; import { useCopyDocLink } from '../hooks'; -import { Doc } from '../types'; +import { Doc, Role } from '../types'; const ModalRemoveDoc = dynamic( () => @@ -88,7 +92,7 @@ interface DocToolBoxProps { export const DocToolBox = ({ doc }: DocToolBoxProps) => { const { t } = useTranslation(); - const treeContext = useTreeContext(); + const treeContext = useTreeContext(); const router = useRouter(); const isTopParent = doc.id === treeContext?.root?.id; // it can be a child but not for the current user const { authenticated } = useAuth(); @@ -98,6 +102,8 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => { const [isModalShareOpen, setIsModalShareOpen] = useState(false); const [isModalHistoryOpen, setIsModalHistoryOpen] = useState(false); const [isModalLeaveOpen, setIsModalLeaveOpen] = useState(false); + const [isModalMoveOpen, setIsModalMoveOpen] = useState(false); + const { mutate: detachDoc } = useDetachDoc(); const { restoreFocus, addLastFocus } = useFocusStore(); const { isMobile } = useResponsiveStore(); @@ -198,6 +204,40 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => { }, isHidden: !doc.abilities.duplicate, }, + { + label: t('Move to my docs'), + isHidden: isTopParent || doc.user_role !== Role.OWNER, + icon: , + callback: () => { + if (!treeContext?.root) { + return; + } + + detachDoc( + { documentId: doc.id, rootId: treeContext.root.id }, + { + onSuccess: () => { + if (treeContext.root) { + treeContext.treeData.setSelectedNode(treeContext.root); + void router.push(`/docs/${treeContext.root.id}`).then(() => { + setTimeout(() => { + treeContext?.treeData.deleteNode(doc.id); + }, 100); + }); + } + }, + }, + ); + }, + }, + { + label: t('Move into a doc'), + icon: , + callback: () => { + setIsModalMoveOpen(true); + }, + isHidden: !doc.abilities.move, + }, { type: 'separator' }, { label: t('History', { @@ -322,6 +362,17 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => { doc={doc} /> )} + {isModalMoveOpen && ( + { + setIsModalMoveOpen(false); + restoreFocus(); + }} + isOpen={isModalMoveOpen} + onAfterMove={() => treeContext?.setRoot(null)} + /> + )} ); }; diff --git a/src/frontend/apps/impress/src/features/docs/docs-grid/components/ModalConfimationMoveDoc.tsx b/src/frontend/apps/impress/src/features/docs/doc-management/components/ModalConfimationMoveDoc.tsx similarity index 100% rename from src/frontend/apps/impress/src/features/docs/docs-grid/components/ModalConfimationMoveDoc.tsx rename to src/frontend/apps/impress/src/features/docs/doc-management/components/ModalConfimationMoveDoc.tsx diff --git a/src/frontend/apps/impress/src/features/docs/doc-tree/assets/doc-extract-bold.svg b/src/frontend/apps/impress/src/features/docs/doc-tree/assets/doc-extract-bold.svg deleted file mode 100644 index 47d4fa1a9..000000000 --- a/src/frontend/apps/impress/src/features/docs/doc-tree/assets/doc-extract-bold.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/src/frontend/apps/impress/src/features/docs/doc-tree/components/DocSubPageItem.tsx b/src/frontend/apps/impress/src/features/docs/doc-tree/components/DocSubPageItem.tsx index b85ceeb0f..17d3e0398 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-tree/components/DocSubPageItem.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-tree/components/DocSubPageItem.tsx @@ -11,7 +11,7 @@ import { useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { css } from 'styled-components'; -import { Box, Icon, StyledLink, Text } from '@/components'; +import { Box, StyledLink, Text } from '@/components'; import { Doc, DocIcon, @@ -294,14 +294,6 @@ const DocSubPageItemContent = (props: TreeViewNodeProps) => { {displayTitle} - {doc.nb_accesses_direct >= 1 && ( - - )} - - + ), callback: handleDetachDoc, }, diff --git a/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocGridContentList.tsx b/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocGridContentList.tsx index 03fea5ac0..78b90c1c3 100644 --- a/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocGridContentList.tsx +++ b/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocGridContentList.tsx @@ -23,9 +23,11 @@ import { Droppable } from './Droppable'; const ModalConfirmationMoveDoc = dynamic( () => - import('./ModalConfimationMoveDoc').then((mod) => ({ - default: mod.ModalConfirmationMoveDoc, - })), + import('@/docs/doc-management/components/ModalConfimationMoveDoc').then( + (mod) => ({ + default: mod.ModalConfirmationMoveDoc, + }), + ), { ssr: false }, ); diff --git a/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocsGridActions.tsx b/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocsGridActions.tsx index 430df4711..6bd5fc709 100644 --- a/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocsGridActions.tsx +++ b/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocsGridActions.tsx @@ -13,7 +13,7 @@ import KeepOffSVG from '@/assets/icons/ui-kit/keep_off.svg'; import LeaveSVG from '@/assets/icons/ui-kit/leave.svg'; import MoreSVG from '@/assets/icons/ui-kit/more_horiz.svg'; import { - Doc, + type Doc, KEY_LIST_DOC, KEY_LIST_FAVORITE_DOC, useCreateFavoriteDoc, @@ -21,11 +21,10 @@ import { useDuplicateDoc, useTrans, } from '@/docs/doc-management'; +import { DocMoveModal } from '@/docs/doc-management/components/DocMoveModal'; import { focusMainContentStart } from '@/layouts/utils'; import { useFocusStore } from '@/stores'; -import { DocMoveModal } from './DocMoveModal'; - const DocShareModal = dynamic( () => import('@/docs/doc-share/components/DocShareModal').then((mod) => ({