🛂(frontend) add confirmation modal on move modal
If the document has more than 1 direct access,we want to display a confirmation modal before moving the document. This is to prevent users from accidentally moving a document that is shared with multiple people. The accesses and invitations will be removed from the document.
This commit is contained in:
@@ -3,10 +3,12 @@ import { expect, test } from '@playwright/test';
|
|||||||
import {
|
import {
|
||||||
createDoc,
|
createDoc,
|
||||||
getGridRow,
|
getGridRow,
|
||||||
|
getOtherBrowserName,
|
||||||
mockedListDocs,
|
mockedListDocs,
|
||||||
toggleHeaderMenu,
|
toggleHeaderMenu,
|
||||||
verifyDocName,
|
verifyDocName,
|
||||||
} from './utils-common';
|
} from './utils-common';
|
||||||
|
import { addNewMember } from './utils-share';
|
||||||
import { createRootSubPage } from './utils-sub-pages';
|
import { createRootSubPage } from './utils-sub-pages';
|
||||||
|
|
||||||
test.describe('Doc grid move', () => {
|
test.describe('Doc grid move', () => {
|
||||||
@@ -178,6 +180,15 @@ test.describe('Doc grid move', () => {
|
|||||||
await page.goto('/');
|
await page.goto('/');
|
||||||
|
|
||||||
const [titleDoc1] = await createDoc(page, 'Draggable doc', browserName, 1);
|
const [titleDoc1] = await createDoc(page, 'Draggable doc', browserName, 1);
|
||||||
|
|
||||||
|
const otherBrowserName = getOtherBrowserName(browserName);
|
||||||
|
await page.getByRole('button', { name: 'Share' }).click();
|
||||||
|
await addNewMember(page, 0, 'Administrator', otherBrowserName);
|
||||||
|
await page
|
||||||
|
.getByRole('dialog')
|
||||||
|
.getByRole('button', { name: 'close' })
|
||||||
|
.click();
|
||||||
|
|
||||||
await page.getByRole('button', { name: 'Back to homepage' }).click();
|
await page.getByRole('button', { name: 'Back to homepage' }).click();
|
||||||
|
|
||||||
const [titleDoc2] = await createDoc(page, 'Droppable doc', browserName, 1);
|
const [titleDoc2] = await createDoc(page, 'Droppable doc', browserName, 1);
|
||||||
@@ -209,6 +220,18 @@ test.describe('Doc grid move', () => {
|
|||||||
// Validate the move action
|
// Validate the move action
|
||||||
await page.keyboard.press('Enter');
|
await page.keyboard.press('Enter');
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page
|
||||||
|
.getByRole('dialog')
|
||||||
|
.getByText('it will lose its current access rights'),
|
||||||
|
).toBeVisible();
|
||||||
|
|
||||||
|
await page
|
||||||
|
.getByRole('dialog')
|
||||||
|
.getByRole('button', { name: 'Move', exact: true })
|
||||||
|
.first()
|
||||||
|
.click();
|
||||||
|
|
||||||
await expect(docsGrid.getByText(titleDoc1)).toBeHidden();
|
await expect(docsGrid.getByText(titleDoc1)).toBeHidden();
|
||||||
await docsGrid
|
await docsGrid
|
||||||
.getByRole('link', { name: `Open document ${titleDoc2}` })
|
.getByRole('link', { name: `Open document ${titleDoc2}` })
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Button, Modal, ModalSize } from '@gouvfr-lasuite/cunningham-react';
|
import { Button, Modal, ModalSize } from '@gouvfr-lasuite/cunningham-react';
|
||||||
import { ReactNode } from 'react';
|
import { ReactNode, useEffect } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import { Box } from '../Box';
|
import { Box } from '../Box';
|
||||||
@@ -25,6 +25,22 @@ export const AlertModal = ({
|
|||||||
title,
|
title,
|
||||||
}: AlertModalProps) => {
|
}: AlertModalProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO:
|
||||||
|
* Remove this effect when Cunningham will have this patch released:
|
||||||
|
* https://github.com/suitenumerique/cunningham/pull/377
|
||||||
|
*/
|
||||||
|
useEffect(() => {
|
||||||
|
const timeout = setTimeout(() => {
|
||||||
|
const contents = document.querySelectorAll('.c__modal__content');
|
||||||
|
contents.forEach((content) => {
|
||||||
|
content.setAttribute('tabindex', '-1');
|
||||||
|
});
|
||||||
|
}, 100);
|
||||||
|
return () => clearTimeout(timeout);
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
@@ -43,12 +59,13 @@ export const AlertModal = ({
|
|||||||
</Text>
|
</Text>
|
||||||
}
|
}
|
||||||
rightActions={
|
rightActions={
|
||||||
<>
|
<Box $direction="row" $gap="small">
|
||||||
<Button
|
<Button
|
||||||
aria-label={`${t('Cancel')} - ${title}`}
|
aria-label={`${t('Cancel')} - ${title}`}
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
fullWidth
|
fullWidth
|
||||||
onClick={() => onClose()}
|
autoFocus
|
||||||
|
onClick={onClose}
|
||||||
>
|
>
|
||||||
{cancelLabel ?? t('Cancel')}
|
{cancelLabel ?? t('Cancel')}
|
||||||
</Button>
|
</Button>
|
||||||
@@ -59,7 +76,7 @@ export const AlertModal = ({
|
|||||||
>
|
>
|
||||||
{confirmLabel ?? t('Confirm')}
|
{confirmLabel ?? t('Confirm')}
|
||||||
</Button>
|
</Button>
|
||||||
</>
|
</Box>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Box className="--docs--alert-modal">
|
<Box className="--docs--alert-modal">
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ export * from './useDocOptions';
|
|||||||
export * from './useDocs';
|
export * from './useDocs';
|
||||||
export * from './useDocsFavorite';
|
export * from './useDocsFavorite';
|
||||||
export * from './useDuplicateDoc';
|
export * from './useDuplicateDoc';
|
||||||
|
export * from './useMoveDoc';
|
||||||
export * from './useRestoreDoc';
|
export * from './useRestoreDoc';
|
||||||
export * from './useSubDocs';
|
export * from './useSubDocs';
|
||||||
export * from './useUpdateDoc';
|
export * from './useUpdateDoc';
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
import { TreeViewMoveModeEnum } from '@gouvfr-lasuite/ui-kit';
|
||||||
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import { APIError, errorCauses, fetchAPI } from '@/api';
|
||||||
|
import {
|
||||||
|
getDocAccesses,
|
||||||
|
getDocInvitations,
|
||||||
|
useDeleteDocAccess,
|
||||||
|
useDeleteDocInvitation,
|
||||||
|
} from '@/docs/doc-share';
|
||||||
|
|
||||||
|
import { KEY_LIST_DOC } from './useDocs';
|
||||||
|
|
||||||
|
export type MoveDocParam = {
|
||||||
|
sourceDocumentId: string;
|
||||||
|
targetDocumentId: string;
|
||||||
|
position: TreeViewMoveModeEnum;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const moveDoc = async ({
|
||||||
|
sourceDocumentId,
|
||||||
|
targetDocumentId,
|
||||||
|
position,
|
||||||
|
}: MoveDocParam): Promise<void> => {
|
||||||
|
const response = await fetchAPI(`documents/${sourceDocumentId}/move/`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({
|
||||||
|
target_document_id: targetDocumentId,
|
||||||
|
position,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new APIError('Failed to move the doc', await errorCauses(response));
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.json() as Promise<void>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function useMoveDoc(deleteAccessOnMove = false) {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
const { mutate: handleDeleteInvitation } = useDeleteDocInvitation();
|
||||||
|
const { mutate: handleDeleteAccess } = useDeleteDocAccess();
|
||||||
|
|
||||||
|
return useMutation<void, APIError, MoveDocParam>({
|
||||||
|
mutationFn: moveDoc,
|
||||||
|
async onSuccess(_data, variables, _onMutateResult, _context) {
|
||||||
|
if (!deleteAccessOnMove) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void queryClient.invalidateQueries({
|
||||||
|
queryKey: [KEY_LIST_DOC],
|
||||||
|
});
|
||||||
|
const accesses = await getDocAccesses({
|
||||||
|
docId: variables.sourceDocumentId,
|
||||||
|
});
|
||||||
|
|
||||||
|
const invitationsResponse = await getDocInvitations({
|
||||||
|
docId: variables.sourceDocumentId,
|
||||||
|
page: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
const invitations = invitationsResponse.results;
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
...invitations.map((invitation) =>
|
||||||
|
handleDeleteInvitation({
|
||||||
|
docId: variables.sourceDocumentId,
|
||||||
|
invitationId: invitation.id,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
...accesses.map((access) =>
|
||||||
|
handleDeleteAccess({
|
||||||
|
docId: variables.sourceDocumentId,
|
||||||
|
accessId: access.id,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -1,3 +1,2 @@
|
|||||||
export * from './useDocChildren';
|
export * from './useDocChildren';
|
||||||
export * from './useDocTree';
|
export * from './useDocTree';
|
||||||
export * from './useMove';
|
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
import { TreeViewMoveModeEnum } from '@gouvfr-lasuite/ui-kit';
|
|
||||||
import { useMutation } from '@tanstack/react-query';
|
|
||||||
|
|
||||||
import { APIError, errorCauses, fetchAPI } from '@/api';
|
|
||||||
|
|
||||||
export type MoveDocParam = {
|
|
||||||
sourceDocumentId: string;
|
|
||||||
targetDocumentId: string;
|
|
||||||
position: TreeViewMoveModeEnum;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const moveDoc = async ({
|
|
||||||
sourceDocumentId,
|
|
||||||
targetDocumentId,
|
|
||||||
position,
|
|
||||||
}: MoveDocParam): Promise<void> => {
|
|
||||||
const response = await fetchAPI(`documents/${sourceDocumentId}/move/`, {
|
|
||||||
method: 'POST',
|
|
||||||
body: JSON.stringify({
|
|
||||||
target_document_id: targetDocumentId,
|
|
||||||
position,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new APIError('Failed to move the doc', await errorCauses(response));
|
|
||||||
}
|
|
||||||
|
|
||||||
return response.json() as Promise<void>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function useMoveDoc() {
|
|
||||||
return useMutation<void, APIError, MoveDocParam>({
|
|
||||||
mutationFn: moveDoc,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -12,10 +12,14 @@ import { css } from 'styled-components';
|
|||||||
|
|
||||||
import { Box, Overlayer, StyledLink } from '@/components';
|
import { Box, Overlayer, StyledLink } from '@/components';
|
||||||
import { useCunninghamTheme } from '@/cunningham';
|
import { useCunninghamTheme } from '@/cunningham';
|
||||||
import { Doc, SimpleDocItem } from '@/docs/doc-management';
|
import {
|
||||||
|
Doc,
|
||||||
|
SimpleDocItem,
|
||||||
|
useMoveDoc,
|
||||||
|
useTrans,
|
||||||
|
} from '@/docs/doc-management';
|
||||||
|
|
||||||
import { KEY_DOC_TREE, useDocTree } from '../api/useDocTree';
|
import { KEY_DOC_TREE, useDocTree } from '../api/useDocTree';
|
||||||
import { useMoveDoc } from '../api/useMove';
|
|
||||||
import { findIndexInTree } from '../utils';
|
import { findIndexInTree } from '../utils';
|
||||||
|
|
||||||
import { DocSubPageItem } from './DocSubPageItem';
|
import { DocSubPageItem } from './DocSubPageItem';
|
||||||
@@ -28,6 +32,7 @@ type DocTreeProps = {
|
|||||||
export const DocTree = ({ currentDoc }: DocTreeProps) => {
|
export const DocTree = ({ currentDoc }: DocTreeProps) => {
|
||||||
const { spacingsTokens } = useCunninghamTheme();
|
const { spacingsTokens } = useCunninghamTheme();
|
||||||
const { isDesktop } = useResponsive();
|
const { isDesktop } = useResponsive();
|
||||||
|
const { untitledDocument } = useTrans();
|
||||||
const [treeRoot, setTreeRoot] = useState<HTMLElement | null>(null);
|
const [treeRoot, setTreeRoot] = useState<HTMLElement | null>(null);
|
||||||
const treeContext = useTreeContext<Doc | null>();
|
const treeContext = useTreeContext<Doc | null>();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@@ -265,7 +270,7 @@ export const DocTree = ({ currentDoc }: DocTreeProps) => {
|
|||||||
ref={rootItemRef}
|
ref={rootItemRef}
|
||||||
data-testid="doc-tree-root-item"
|
data-testid="doc-tree-root-item"
|
||||||
role="treeitem"
|
role="treeitem"
|
||||||
aria-label={`${t('Root document {{title}}', { title: treeContext.root?.title || t('Untitled document') })}`}
|
aria-label={`${t('Root document {{title}}', { title: treeContext.root?.title || untitledDocument })}`}
|
||||||
aria-selected={rootIsSelected}
|
aria-selected={rootIsSelected}
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
onFocus={handleRootFocus}
|
onFocus={handleRootFocus}
|
||||||
@@ -325,7 +330,7 @@ export const DocTree = ({ currentDoc }: DocTreeProps) => {
|
|||||||
);
|
);
|
||||||
router.push(`/docs/${treeContext?.root?.id}`);
|
router.push(`/docs/${treeContext?.root?.id}`);
|
||||||
}}
|
}}
|
||||||
aria-label={`${t('Open root document')}: ${treeContext.root?.title || t('Untitled document')}`}
|
aria-label={`${t('Open root document')}: ${treeContext.root?.title || untitledDocument}`}
|
||||||
tabIndex={-1} // avoid double tabstop
|
tabIndex={-1} // avoid double tabstop
|
||||||
>
|
>
|
||||||
<Box $direction="row" $align="center" $width="100%">
|
<Box $direction="row" $align="center" $width="100%">
|
||||||
|
|||||||
@@ -2,19 +2,11 @@ import { DndContext, DragOverlay, Modifier } from '@dnd-kit/core';
|
|||||||
import { getEventCoordinates } from '@dnd-kit/utilities';
|
import { getEventCoordinates } from '@dnd-kit/utilities';
|
||||||
import { useModal } from '@gouvfr-lasuite/cunningham-react';
|
import { useModal } from '@gouvfr-lasuite/cunningham-react';
|
||||||
import { TreeViewMoveModeEnum } from '@gouvfr-lasuite/ui-kit';
|
import { TreeViewMoveModeEnum } from '@gouvfr-lasuite/ui-kit';
|
||||||
import { useQueryClient } from '@tanstack/react-query';
|
|
||||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { Trans, useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import { AlertModal, Card, Text } from '@/components';
|
import { Card, Text } from '@/components';
|
||||||
import { Doc, KEY_LIST_DOC, useTrans } from '@/docs/doc-management';
|
import { Doc, useMoveDoc, useTrans } from '@/docs/doc-management';
|
||||||
import {
|
|
||||||
getDocAccesses,
|
|
||||||
getDocInvitations,
|
|
||||||
useDeleteDocAccess,
|
|
||||||
useDeleteDocInvitation,
|
|
||||||
} from '@/docs/doc-share';
|
|
||||||
import { useMoveDoc } from '@/docs/doc-tree';
|
|
||||||
import { useResponsiveStore } from '@/stores/useResponsiveStore';
|
import { useResponsiveStore } from '@/stores/useResponsiveStore';
|
||||||
|
|
||||||
import { DocDragEndData, useDragAndDrop } from '../hooks/useDragAndDrop';
|
import { DocDragEndData, useDragAndDrop } from '../hooks/useDragAndDrop';
|
||||||
@@ -22,6 +14,7 @@ import { DocDragEndData, useDragAndDrop } from '../hooks/useDragAndDrop';
|
|||||||
import { DocsGridItem } from './DocsGridItem';
|
import { DocsGridItem } from './DocsGridItem';
|
||||||
import { Draggable } from './Draggable';
|
import { Draggable } from './Draggable';
|
||||||
import { Droppable } from './Droppable';
|
import { Droppable } from './Droppable';
|
||||||
|
import { ModalConfirmationMoveDoc } from './ModalConfimationMoveDoc';
|
||||||
|
|
||||||
const snapToTopLeft: Modifier = ({
|
const snapToTopLeft: Modifier = ({
|
||||||
activatorEvent,
|
activatorEvent,
|
||||||
@@ -55,11 +48,8 @@ type DocGridContentListProps = {
|
|||||||
export const DraggableDocGridContentList = ({
|
export const DraggableDocGridContentList = ({
|
||||||
docs,
|
docs,
|
||||||
}: DocGridContentListProps) => {
|
}: DocGridContentListProps) => {
|
||||||
const { mutateAsync: handleMove, isError } = useMoveDoc();
|
const { mutateAsync: handleMove, isError } = useMoveDoc(true);
|
||||||
const queryClient = useQueryClient();
|
|
||||||
const modalConfirmation = useModal();
|
const modalConfirmation = useModal();
|
||||||
const { mutate: handleDeleteInvitation } = useDeleteDocInvitation();
|
|
||||||
const { mutate: handleDeleteAccess } = useDeleteDocAccess();
|
|
||||||
const onDragData = useRef<DocDragEndData | null>(null);
|
const onDragData = useRef<DocDragEndData | null>(null);
|
||||||
const { untitledDocument } = useTrans();
|
const { untitledDocument } = useTrans();
|
||||||
|
|
||||||
@@ -82,35 +72,6 @@ export const DraggableDocGridContentList = ({
|
|||||||
targetDocumentId,
|
targetDocumentId,
|
||||||
position: TreeViewMoveModeEnum.FIRST_CHILD,
|
position: TreeViewMoveModeEnum.FIRST_CHILD,
|
||||||
});
|
});
|
||||||
|
|
||||||
void queryClient.invalidateQueries({
|
|
||||||
queryKey: [KEY_LIST_DOC],
|
|
||||||
});
|
|
||||||
const accesses = await getDocAccesses({
|
|
||||||
docId: sourceDocumentId,
|
|
||||||
});
|
|
||||||
|
|
||||||
const invitationsResponse = await getDocInvitations({
|
|
||||||
docId: sourceDocumentId,
|
|
||||||
page: 1,
|
|
||||||
});
|
|
||||||
|
|
||||||
const invitations = invitationsResponse.results;
|
|
||||||
|
|
||||||
await Promise.all([
|
|
||||||
...invitations.map((invitation) =>
|
|
||||||
handleDeleteInvitation({
|
|
||||||
docId: sourceDocumentId,
|
|
||||||
invitationId: invitation.id,
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
...accesses.map((access) =>
|
|
||||||
handleDeleteAccess({
|
|
||||||
docId: sourceDocumentId,
|
|
||||||
accessId: access.id,
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
]);
|
|
||||||
} finally {
|
} finally {
|
||||||
onDragData.current = null;
|
onDragData.current = null;
|
||||||
}
|
}
|
||||||
@@ -207,25 +168,13 @@ export const DraggableDocGridContentList = ({
|
|||||||
</DragOverlay>
|
</DragOverlay>
|
||||||
</DndContext>
|
</DndContext>
|
||||||
{modalConfirmation.isOpen && (
|
{modalConfirmation.isOpen && (
|
||||||
<AlertModal
|
<ModalConfirmationMoveDoc
|
||||||
{...modalConfirmation}
|
isOpen={modalConfirmation.isOpen}
|
||||||
title={t('Move document')}
|
onClose={modalConfirmation.onClose}
|
||||||
description={
|
onConfirm={handleMoveDoc}
|
||||||
<Text $display="inline">
|
targetDocumentTitle={
|
||||||
<Trans
|
onDragData.current?.target.title || untitledDocument
|
||||||
i18nKey="By moving this document to <strong>{{targetDocumentTitle}}</strong>, it will lose its current access rights and inherit the permissions of that document. <strong>This access change cannot be undone.</strong>"
|
|
||||||
values={{
|
|
||||||
targetDocumentTitle:
|
|
||||||
onDragData.current?.target.title ?? untitledDocument,
|
|
||||||
}}
|
|
||||||
components={{ strong: <strong /> }}
|
|
||||||
/>
|
|
||||||
</Text>
|
|
||||||
}
|
}
|
||||||
confirmLabel={t('Move')}
|
|
||||||
onConfirm={() => {
|
|
||||||
void handleMoveDoc();
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
import { Button, Modal, ModalSize } from '@gouvfr-lasuite/cunningham-react';
|
import {
|
||||||
|
Button,
|
||||||
|
Modal,
|
||||||
|
ModalSize,
|
||||||
|
useModal,
|
||||||
|
} from '@gouvfr-lasuite/cunningham-react';
|
||||||
import { TreeViewMoveModeEnum } from '@gouvfr-lasuite/ui-kit';
|
import { TreeViewMoveModeEnum } from '@gouvfr-lasuite/ui-kit';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
@@ -14,6 +19,7 @@ import EmptySearchIcon from '@/docs/doc-search/assets/illustration-docs-empty.pn
|
|||||||
import { useResponsiveStore } from '@/stores';
|
import { useResponsiveStore } from '@/stores';
|
||||||
|
|
||||||
import { DocsGridItemDate, DocsGridItemTitle } from './DocsGridItem';
|
import { DocsGridItemDate, DocsGridItemTitle } from './DocsGridItem';
|
||||||
|
import { ModalConfirmationMoveDoc } from './ModalConfimationMoveDoc';
|
||||||
|
|
||||||
export const DocMoveModalStyle = createGlobalStyle`
|
export const DocMoveModalStyle = createGlobalStyle`
|
||||||
.c__modal--full .c__modal__scroller {
|
.c__modal--full .c__modal__scroller {
|
||||||
@@ -67,6 +73,8 @@ export const DocMoveModal = ({
|
|||||||
const [docSelected, setDocSelected] = useState<Doc>();
|
const [docSelected, setDocSelected] = useState<Doc>();
|
||||||
const { untitledDocument } = useTrans();
|
const { untitledDocument } = useTrans();
|
||||||
const docTitle = doc.title || untitledDocument;
|
const docTitle = doc.title || untitledDocument;
|
||||||
|
const docTargetTitle = docSelected?.title || untitledDocument;
|
||||||
|
const modalConfirmation = useModal();
|
||||||
const { mutate: moveDoc } = useMoveDoc(true);
|
const { mutate: moveDoc } = useMoveDoc(true);
|
||||||
const [search, setSearch] = useState('');
|
const [search, setSearch] = useState('');
|
||||||
const { isDesktop } = useResponsiveStore();
|
const { isDesktop } = useResponsiveStore();
|
||||||
@@ -77,6 +85,7 @@ export const DocMoveModal = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleMoveDoc = () => {
|
const handleMoveDoc = () => {
|
||||||
|
modalConfirmation.onClose();
|
||||||
if (!docSelected?.id) {
|
if (!docSelected?.id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -105,6 +114,11 @@ export const DocMoveModal = ({
|
|||||||
variant="primary"
|
variant="primary"
|
||||||
fullWidth
|
fullWidth
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
if (doc.nb_accesses_direct > 1) {
|
||||||
|
modalConfirmation.open();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
handleMoveDoc();
|
handleMoveDoc();
|
||||||
}}
|
}}
|
||||||
disabled={!docSelected}
|
disabled={!docSelected}
|
||||||
@@ -255,6 +269,14 @@ export const DocMoveModal = ({
|
|||||||
</QuickSearch>
|
</QuickSearch>
|
||||||
</Box>
|
</Box>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
{modalConfirmation.isOpen && (
|
||||||
|
<ModalConfirmationMoveDoc
|
||||||
|
isOpen={modalConfirmation.isOpen}
|
||||||
|
onClose={modalConfirmation.onClose}
|
||||||
|
onConfirm={handleMoveDoc}
|
||||||
|
targetDocumentTitle={docTargetTitle}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import { Trans, useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
import { AlertModal, Text } from '@/components';
|
||||||
|
|
||||||
|
interface ModalConfirmationMoveDocProps {
|
||||||
|
targetDocumentTitle: string;
|
||||||
|
onConfirm: () => void;
|
||||||
|
onClose: () => void;
|
||||||
|
isOpen: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ModalConfirmationMoveDoc = ({
|
||||||
|
targetDocumentTitle,
|
||||||
|
onClose,
|
||||||
|
onConfirm,
|
||||||
|
isOpen,
|
||||||
|
}: ModalConfirmationMoveDocProps) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AlertModal
|
||||||
|
onClose={onClose}
|
||||||
|
isOpen={isOpen}
|
||||||
|
title={t('Move document')}
|
||||||
|
aria-label={t('Modal confirmation for moving a document')}
|
||||||
|
description={
|
||||||
|
<Text $display="inline">
|
||||||
|
<Trans
|
||||||
|
i18nKey="By moving this document to <strong>{{targetDocumentTitle}}</strong>, it will lose its current access rights and inherit the permissions of that document. <strong>This access change cannot be undone.</strong>"
|
||||||
|
values={{
|
||||||
|
targetDocumentTitle,
|
||||||
|
}}
|
||||||
|
components={{ strong: <strong /> }}
|
||||||
|
/>
|
||||||
|
</Text>
|
||||||
|
}
|
||||||
|
confirmLabel={t('Move')}
|
||||||
|
onConfirm={onConfirm}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user