diff --git a/CHANGELOG.md b/CHANGELOG.md
index b19610696..9e1f33118 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,8 @@ and this project adheres to
- ✨(ci) add security scan #291
- ✨(frontend) Activate versions feature #240
+- ✨(frontend) one-click document creation #275
+- ✨(frontend) edit title inline #275
## Changed
diff --git a/src/frontend/apps/e2e/__tests__/app-impress/common.ts b/src/frontend/apps/e2e/__tests__/app-impress/common.ts
index 1cab233f8..c09bc7138 100644
--- a/src/frontend/apps/e2e/__tests__/app-impress/common.ts
+++ b/src/frontend/apps/e2e/__tests__/app-impress/common.ts
@@ -29,32 +29,21 @@ export const createDoc = async (
length: number,
isPublic: boolean = false,
) => {
- const buttonCreate = page.getByRole('button', {
- name: 'Create the document',
- });
-
const randomDocs = randomName(docName, browserName, length);
for (let i = 0; i < randomDocs.length; i++) {
const header = page.locator('header').first();
await header.locator('h2').getByText('Docs').click();
- const buttonCreateHomepage = page.getByRole('button', {
- name: 'Create a new document',
- });
- await buttonCreateHomepage.click();
-
- // Fill input
await page
- .getByRole('textbox', {
- name: 'Document name',
+ .getByRole('button', {
+ name: 'Create a new document',
})
- .fill(randomDocs[i]);
-
- await expect(buttonCreate).toBeEnabled();
- await buttonCreate.click();
+ .click();
- await expect(page.locator('h2').getByText(randomDocs[i])).toBeVisible();
+ await page.getByRole('heading', { name: 'Untitled document' }).click();
+ await page.keyboard.type(randomDocs[i]);
+ await page.getByText('Created at ').click();
if (isPublic) {
await page.getByRole('button', { name: 'Share' }).click();
diff --git a/src/frontend/apps/e2e/__tests__/app-impress/doc-create.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/doc-create.spec.ts
index 6fab7777d..f0fcd335b 100644
--- a/src/frontend/apps/e2e/__tests__/app-impress/doc-create.spec.ts
+++ b/src/frontend/apps/e2e/__tests__/app-impress/doc-create.spec.ts
@@ -7,48 +7,12 @@ test.beforeEach(async ({ page }) => {
});
test.describe('Doc Create', () => {
- test('checks all the create doc elements are visible', async ({ page }) => {
- const buttonCreateHomepage = page.getByRole('button', {
- name: 'Create a new document',
- });
- await buttonCreateHomepage.click();
- await expect(buttonCreateHomepage).toBeHidden();
-
- const card = page.getByRole('dialog').first();
-
- await expect(
- card.locator('h2').getByText('Create a new document'),
- ).toBeVisible();
- await expect(card.getByLabel('Document name')).toBeVisible();
-
- await expect(
- card.getByRole('button', {
- name: 'Create the document',
- }),
- ).toBeVisible();
-
- await expect(card.getByLabel('Close the modal')).toBeVisible();
- });
-
- test('checks the cancel button interaction', async ({ page }) => {
- const buttonCreateHomepage = page.getByRole('button', {
- name: 'Create a new document',
- });
- await buttonCreateHomepage.click();
- await expect(buttonCreateHomepage).toBeHidden();
-
- const card = page.getByRole('dialog').first();
-
- await card.getByLabel('Close the modal').click();
-
- await expect(buttonCreateHomepage).toBeVisible();
- });
-
test('it creates a doc', async ({ page, browserName }) => {
const [docTitle] = await createDoc(page, 'My new doc', browserName, 1);
- expect(await page.locator('title').textContent()).toMatch(
- /My new doc - Docs/,
+ await page.waitForFunction(
+ () => document.title.match(/My new doc - Docs/),
+ { timeout: 5000 },
);
const header = page.locator('header').first();
@@ -59,7 +23,8 @@ test.describe('Doc Create', () => {
.getByRole('table');
await expect(datagrid.getByLabel('Loading data')).toBeHidden();
-
- await expect(datagrid.getByText(docTitle)).toBeVisible();
+ await expect(datagrid.getByText(docTitle)).toBeVisible({
+ timeout: 5000,
+ });
});
});
diff --git a/src/frontend/apps/e2e/__tests__/app-impress/doc-editor.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/doc-editor.spec.ts
index c5da0d512..a39362e23 100644
--- a/src/frontend/apps/e2e/__tests__/app-impress/doc-editor.spec.ts
+++ b/src/frontend/apps/e2e/__tests__/app-impress/doc-editor.spec.ts
@@ -40,19 +40,18 @@ test.describe('Doc Editor', () => {
await expect(page.locator('h2').getByText(randomDoc[0])).toBeVisible();
- await page.locator('.ProseMirror.bn-editor').click();
- await page
- .locator('.ProseMirror.bn-editor')
- .fill('[test markdown](http://test-markdown.html)');
+ const editor = page.locator('.ProseMirror');
+ await editor.click();
+ await editor.fill('[test markdown](http://test-markdown.html)');
- await expect(page.getByText('[test markdown]')).toBeVisible();
+ await expect(editor.getByText('[test markdown]')).toBeVisible();
- await page.getByText('[test markdown]').dblclick();
+ await editor.getByText('[test markdown]').dblclick();
await page.locator('button[data-test="convertMarkdown"]').click();
- await expect(page.getByText('[test markdown]')).toBeHidden();
+ await expect(editor.getByText('[test markdown]')).toBeHidden();
await expect(
- page.getByRole('link', {
+ editor.getByRole('link', {
name: 'test markdown',
}),
).toHaveAttribute('href', 'http://test-markdown.html');
@@ -64,38 +63,40 @@ test.describe('Doc Editor', () => {
// Check the first doc
const firstDoc = await goToGridDoc(page);
await expect(page.locator('h2').getByText(firstDoc)).toBeVisible();
- await page.locator('.ProseMirror.bn-editor').click();
- await page.locator('.ProseMirror.bn-editor').fill('Hello World Doc 1');
- await expect(page.getByText('Hello World Doc 1')).toBeVisible();
+
+ const editor = page.locator('.ProseMirror');
+ await editor.click();
+ await editor.fill('Hello World Doc 1');
+ await expect(editor.getByText('Hello World Doc 1')).toBeVisible();
// Check the second doc
const secondDoc = await goToGridDoc(page, {
nthRow: 2,
});
await expect(page.locator('h2').getByText(secondDoc)).toBeVisible();
- await expect(page.getByText('Hello World Doc 1')).toBeHidden();
- await page.locator('.ProseMirror.bn-editor').click();
- await page.locator('.ProseMirror.bn-editor').fill('Hello World Doc 2');
- await expect(page.getByText('Hello World Doc 2')).toBeVisible();
+ await expect(editor.getByText('Hello World Doc 1')).toBeHidden();
+ await editor.click();
+ await editor.fill('Hello World Doc 2');
+ await expect(editor.getByText('Hello World Doc 2')).toBeVisible();
// Check the first doc again
await goToGridDoc(page, {
title: firstDoc,
});
await expect(page.locator('h2').getByText(firstDoc)).toBeVisible();
- await expect(page.getByText('Hello World Doc 2')).toBeHidden();
- await expect(page.getByText('Hello World Doc 1')).toBeVisible();
+ await expect(editor.getByText('Hello World Doc 2')).toBeHidden();
+ await expect(editor.getByText('Hello World Doc 1')).toBeVisible();
});
test('it saves the doc when we change pages', async ({ page }) => {
// Check the first doc
const doc = await goToGridDoc(page);
await expect(page.locator('h2').getByText(doc)).toBeVisible();
- await page.locator('.ProseMirror.bn-editor').click();
- await page
- .locator('.ProseMirror.bn-editor')
- .fill('Hello World Doc persisted 1');
- await expect(page.getByText('Hello World Doc persisted 1')).toBeVisible();
+
+ const editor = page.locator('.ProseMirror');
+ await editor.click();
+ await editor.fill('Hello World Doc persisted 1');
+ await expect(editor.getByText('Hello World Doc persisted 1')).toBeVisible();
const secondDoc = await goToGridDoc(page, {
nthRow: 2,
@@ -107,7 +108,7 @@ test.describe('Doc Editor', () => {
title: doc,
});
- await expect(page.getByText('Hello World Doc persisted 1')).toBeVisible();
+ await expect(editor.getByText('Hello World Doc persisted 1')).toBeVisible();
});
test('it saves the doc when we quit pages', async ({ page, browserName }) => {
@@ -117,11 +118,11 @@ test.describe('Doc Editor', () => {
// Check the first doc
const doc = await goToGridDoc(page);
await expect(page.locator('h2').getByText(doc)).toBeVisible();
- await page.locator('.ProseMirror.bn-editor').click();
- await page
- .locator('.ProseMirror.bn-editor')
- .fill('Hello World Doc persisted 2');
- await expect(page.getByText('Hello World Doc persisted 2')).toBeVisible();
+
+ const editor = page.locator('.ProseMirror');
+ await editor.click();
+ await editor.fill('Hello World Doc persisted 2');
+ await expect(editor.getByText('Hello World Doc persisted 2')).toBeVisible();
await page.goto('/');
@@ -129,7 +130,7 @@ test.describe('Doc Editor', () => {
title: doc,
});
- await expect(page.getByText('Hello World Doc persisted 2')).toBeVisible();
+ await expect(editor.getByText('Hello World Doc persisted 2')).toBeVisible();
});
test('it cannot edit if viewer', async ({ page }) => {
diff --git a/src/frontend/apps/e2e/__tests__/app-impress/doc-grid.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/doc-grid.spec.ts
index 35eec8a3d..c83ed044c 100644
--- a/src/frontend/apps/e2e/__tests__/app-impress/doc-grid.spec.ts
+++ b/src/frontend/apps/e2e/__tests__/app-impress/doc-grid.spec.ts
@@ -212,26 +212,6 @@ test.describe('Documents Grid', () => {
).toHaveText(/.*/);
});
- test('it updates document', async ({ page }) => {
- const datagrid = page
- .getByLabel('Datagrid of the documents page 1')
- .getByRole('table');
-
- const docRow = datagrid.getByRole('row').nth(1).getByRole('cell');
-
- const docName = await docRow.nth(1).textContent();
-
- await docRow.getByLabel('Open the document options').click();
-
- await page.getByText('Update document').click();
-
- await page.getByLabel('Document name').fill(`${docName} updated`);
-
- await page.getByText('Validate the modification').click();
-
- await expect(datagrid.getByText(`${docName} updated`)).toBeVisible();
- });
-
test('it deletes the document', async ({ page }) => {
const datagrid = page
.getByLabel('Datagrid of the documents page 1')
@@ -241,11 +221,9 @@ test.describe('Documents Grid', () => {
const docName = await docRow.nth(1).textContent();
- await docRow.getByLabel('Open the document options').click();
-
- await page
+ await docRow
.getByRole('button', {
- name: 'Delete document',
+ name: 'Delete the document',
})
.click();
diff --git a/src/frontend/apps/e2e/__tests__/app-impress/doc-header.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/doc-header.spec.ts
index 9d1a4d1be..ced6677fe 100644
--- a/src/frontend/apps/e2e/__tests__/app-impress/doc-header.spec.ts
+++ b/src/frontend/apps/e2e/__tests__/app-impress/doc-header.spec.ts
@@ -65,49 +65,66 @@ test.describe('Doc Header', () => {
await expect(page.getByRole('button', { name: 'Share' })).toBeVisible();
});
- test('it updates the doc', async ({ page, browserName }) => {
+ test('it updates the title doc', async ({ page, browserName }) => {
const [randomDoc] = await createDoc(page, 'doc-update', browserName, 1);
- await expect(page.locator('h2').getByText(randomDoc)).toBeVisible();
- await page.getByLabel('Open the document options').click();
+ await page.getByRole('heading', { name: randomDoc }).fill(' ');
+ await page.getByText('Created at').click();
+
+ await expect(
+ page.getByRole('heading', { name: 'Untitled document' }),
+ ).toBeVisible();
+ });
+
+ test('it updates the title doc from editor heading', async ({ page }) => {
await page
.getByRole('button', {
- name: 'Update document',
+ name: 'Create a new document',
})
.click();
+ const docHeader = page.getByLabel(
+ 'It is the card information about the document.',
+ );
+
await expect(
- page.locator('h2').getByText(`Update document "${randomDoc}"`),
+ docHeader.getByRole('heading', { name: 'Untitled document', level: 2 }),
).toBeVisible();
- await page.getByText('Document name').fill(`${randomDoc}-updated`);
+ const editor = page.locator('.ProseMirror');
- await page
- .getByRole('button', {
- name: 'Validate the modification',
- })
- .click();
+ await editor.locator('h1').click();
+ await page.keyboard.type('Hello World', { delay: 100 });
await expect(
- page.getByText('The document has been updated.'),
+ docHeader.getByRole('heading', { name: 'Hello World', level: 2 }),
).toBeVisible();
- const docTitle = await goToGridDoc(page, {
- title: `${randomDoc}-updated`,
- });
+ await expect(
+ page.getByText('Document title updated successfully'),
+ ).toBeVisible();
- await expect(page.locator('h2').getByText(docTitle)).toBeVisible();
+ await docHeader
+ .getByRole('heading', { name: 'Hello World', level: 2 })
+ .fill('Top World');
- await page.getByLabel('Open the document options').click();
- await page
- .getByRole('button', {
- name: 'Update document',
- })
- .click();
+ await editor.locator('h1').fill('Super World');
+
+ await expect(
+ docHeader.getByRole('heading', { name: 'Top World', level: 2 }),
+ ).toBeVisible();
+
+ await editor.locator('h1').fill('');
+
+ await docHeader
+ .getByRole('heading', { name: 'Top World', level: 2 })
+ .fill(' ');
+
+ await page.getByText('Created at').click();
await expect(
- page.getByRole('textbox', { name: 'Document name' }),
- ).toHaveValue(`${randomDoc}-updated`);
+ docHeader.getByRole('heading', { name: 'Untitled document', level: 2 }),
+ ).toBeVisible();
});
test('it deletes the doc', async ({ page, browserName }) => {
@@ -167,16 +184,15 @@ test.describe('Doc Header', () => {
await goToGridDoc(page);
- await expect(page.locator('h2').getByText('Mocked document')).toBeVisible();
+ await expect(
+ page.locator('h2').getByText('Mocked document'),
+ ).toHaveAttribute('contenteditable');
await expect(page.getByRole('button', { name: 'Share' })).toBeVisible();
await page.getByLabel('Open the document options').click();
await expect(page.getByRole('button', { name: 'Export' })).toBeVisible();
- await expect(
- page.getByRole('button', { name: 'Update document' }),
- ).toBeVisible();
await expect(
page.getByRole('button', { name: 'Delete document' }),
).toBeHidden();
@@ -199,16 +215,15 @@ test.describe('Doc Header', () => {
await goToGridDoc(page);
- await expect(page.locator('h2').getByText('Mocked document')).toBeVisible();
+ await expect(
+ page.locator('h2').getByText('Mocked document'),
+ ).toHaveAttribute('contenteditable');
await expect(page.getByRole('button', { name: 'Share' })).toBeHidden();
await page.getByLabel('Open the document options').click();
await expect(page.getByRole('button', { name: 'Export' })).toBeVisible();
- await expect(
- page.getByRole('button', { name: 'Update document' }),
- ).toBeVisible();
await expect(
page.getByRole('button', { name: 'Delete document' }),
).toBeHidden();
@@ -231,7 +246,9 @@ test.describe('Doc Header', () => {
await goToGridDoc(page);
- await expect(page.locator('h2').getByText('Mocked document')).toBeVisible();
+ await expect(
+ page.locator('h2').getByText('Mocked document'),
+ ).not.toHaveAttribute('contenteditable');
await expect(page.getByRole('button', { name: 'Share' })).toBeHidden();
@@ -239,9 +256,6 @@ test.describe('Doc Header', () => {
await expect(page.getByRole('button', { name: 'Share' })).toBeHidden();
await expect(page.getByRole('button', { name: 'Export' })).toBeVisible();
- await expect(
- page.getByRole('button', { name: 'Update document' }),
- ).toBeHidden();
await expect(
page.getByRole('button', { name: 'Delete document' }),
).toBeHidden();
diff --git a/src/frontend/apps/e2e/__tests__/app-impress/doc-version.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/doc-version.spec.ts
index e853dda59..bca0fc382 100644
--- a/src/frontend/apps/e2e/__tests__/app-impress/doc-version.spec.ts
+++ b/src/frontend/apps/e2e/__tests__/app-impress/doc-version.spec.ts
@@ -105,7 +105,8 @@ test.describe('Doc Version', () => {
title: randomDoc,
});
- await expect(page.getByText('Hello')).toBeVisible();
+ const editor = page.locator('.ProseMirror');
+ await expect(editor.getByText('Hello')).toBeVisible();
await page.locator('.bn-block-outer').last().click();
await page.keyboard.press('Enter');
await page.locator('.bn-block-outer').last().fill('World');
@@ -153,23 +154,24 @@ test.describe('Doc Version', () => {
await expect(page.locator('h2').getByText(randomDoc)).toBeVisible();
- await page.locator('.bn-block-outer').last().click();
- await page.locator('.bn-block-outer').last().fill('Hello');
+ const editor = page.locator('.ProseMirror');
+ await editor.locator('.bn-block-outer').last().click();
+ await editor.locator('.bn-block-outer').last().fill('Hello');
await goToGridDoc(page, {
title: randomDoc,
});
- await expect(page.getByText('Hello')).toBeVisible();
- await page.locator('.bn-block-outer').last().click();
+ await expect(editor.getByText('Hello')).toBeVisible();
+ await editor.locator('.bn-block-outer').last().click();
await page.keyboard.press('Enter');
- await page.locator('.bn-block-outer').last().fill('World');
+ await editor.locator('.bn-block-outer').last().fill('World');
await goToGridDoc(page, {
title: randomDoc,
});
- await expect(page.getByText('World')).toBeVisible();
+ await expect(editor.getByText('World')).toBeVisible();
await page.getByLabel('Open the document options').click();
await page
@@ -180,7 +182,7 @@ test.describe('Doc Version', () => {
const panel = page.getByLabel('Document panel');
await panel.locator('li').nth(1).click();
- await expect(page.getByText('World')).toBeHidden();
+ await expect(editor.getByText('World')).toBeHidden();
await page
.getByRole('button', {
@@ -199,7 +201,7 @@ test.describe('Doc Version', () => {
await expect(panel.locator('li')).toHaveCount(3);
await panel.getByText('Current version').click();
- await expect(page.getByText('Hello')).toBeVisible();
- await expect(page.getByText('World')).toBeHidden();
+ await expect(editor.getByText('Hello')).toBeVisible();
+ await expect(editor.getByText('World')).toBeHidden();
});
});
diff --git a/src/frontend/apps/impress/src/__tests__/pages.test.tsx b/src/frontend/apps/impress/src/__tests__/pages.test.tsx
index 140932bc5..60d1e1ea4 100644
--- a/src/frontend/apps/impress/src/__tests__/pages.test.tsx
+++ b/src/frontend/apps/impress/src/__tests__/pages.test.tsx
@@ -5,6 +5,14 @@ import { AppWrapper } from '@/tests/utils';
import Page from '../pages';
+jest.mock('next/navigation', () => ({
+ useRouter() {
+ return {
+ push: jest.fn(),
+ };
+ },
+}));
+
describe('Page', () => {
it('checks Page rendering', () => {
render(, { wrapper: AppWrapper });
diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteEditor.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteEditor.tsx
index 8895e41f1..5d531a057 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteEditor.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteEditor.tsx
@@ -13,7 +13,7 @@ import { Version } from '@/features/docs/doc-versioning/';
import { useCreateDocAttachment } from '../api/useCreateDocUpload';
import useSaveDoc from '../hook/useSaveDoc';
-import { useDocStore } from '../stores';
+import { useDocStore, useHeadingStore } from '../stores';
import { randomColor } from '../utils';
import { BlockNoteToolbar } from './BlockNoteToolbar';
@@ -78,6 +78,7 @@ export const BlockNoteContent = ({
isError: isErrorAttachment,
error: errorAttachment,
} = useCreateDocAttachment();
+ const { setHeadings, resetHeadings } = useHeadingStore();
const uploadFile = useCallback(
async (file: File) => {
@@ -116,6 +117,18 @@ export const BlockNoteContent = ({
setStore(storeId, { editor });
}, [setStore, storeId, editor]);
+ useEffect(() => {
+ setHeadings(editor);
+
+ editor?.onEditorContentChange(() => {
+ setHeadings(editor);
+ });
+
+ return () => {
+ resetHeadings();
+ };
+ }, [editor, resetHeadings, setHeadings]);
+
return (
{isErrorAttachment && (
diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteToolbar.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteToolbar.tsx
index 785f84c27..d7deeee2c 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteToolbar.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteToolbar.tsx
@@ -9,12 +9,10 @@ import {
NestBlockButton,
TextAlignButton,
UnnestBlockButton,
- useBlockNoteEditor,
- useComponentsContext,
- useSelectedBlocks,
} from '@blocknote/react';
-import { forEach, isArray } from 'lodash';
-import React, { useMemo } from 'react';
+import React from 'react';
+
+import { MarkdownButton } from './MarkdownButton';
export const BlockNoteToolbar = () => {
return (
@@ -57,79 +55,3 @@ export const BlockNoteToolbar = () => {
/>
);
};
-
-type Block = {
- type: string;
- text: string;
- content: Block[];
-};
-
-function isBlock(block: Block): block is Block {
- return (
- block.content &&
- isArray(block.content) &&
- block.content.length > 0 &&
- typeof block.type !== 'undefined'
- );
-}
-
-const recursiveContent = (content: Block[], base: string = '') => {
- let fullContent = base;
- for (const innerContent of content) {
- if (innerContent.type === 'text') {
- fullContent += innerContent.text;
- } else if (isBlock(innerContent)) {
- fullContent = recursiveContent(innerContent.content, fullContent);
- }
- }
-
- return fullContent;
-};
-
-/**
- * Custom Formatting Toolbar Button to convert markdown to json.
- */
-export function MarkdownButton() {
- const editor = useBlockNoteEditor();
- const Components = useComponentsContext();
- const selectedBlocks = useSelectedBlocks(editor);
-
- const handleConvertMarkdown = () => {
- const blocks = editor.getSelection()?.blocks;
-
- forEach(blocks, async (block) => {
- if (!isBlock(block as unknown as Block)) {
- return;
- }
-
- try {
- const fullContent = recursiveContent(
- block.content as unknown as Block[],
- );
-
- const blockMarkdown =
- await editor.tryParseMarkdownToBlocks(fullContent);
- editor.replaceBlocks([block.id], blockMarkdown);
- } catch (error) {
- console.error('Error parsing Markdown:', error);
- }
- });
- };
-
- const show = useMemo(() => {
- return !!selectedBlocks.find((block) => block.content !== undefined);
- }, [selectedBlocks]);
-
- if (!show || !editor.isEditable || !Components) {
- return null;
- }
-
- return (
-
- M
-
- );
-}
diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/DocEditor.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/DocEditor.tsx
index 58342c3c5..c1f4198dd 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/DocEditor.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/DocEditor.tsx
@@ -8,9 +8,10 @@ import { Box, Card, Text, TextErrors } from '@/components';
import { useCunninghamTheme } from '@/cunningham';
import { DocHeader } from '@/features/docs/doc-header';
import { Doc } from '@/features/docs/doc-management';
-import { useHeading } from '@/features/docs/doc-table-content';
import { Versions, useDocVersion } from '@/features/docs/doc-versioning/';
+import { useHeadingStore } from '../stores';
+
import { BlockNoteEditor } from './BlockNoteEditor';
import { IconOpenPanelEditor, PanelEditor } from './PanelEditor';
@@ -23,7 +24,7 @@ export const DocEditor = ({ doc }: DocEditorProps) => {
query: { versionId },
} = useRouter();
const { t } = useTranslation();
- const headings = useHeading(doc.id);
+ const { headings } = useHeadingStore();
const isVersion = versionId && typeof versionId === 'string';
diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/MarkdownButton.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/MarkdownButton.tsx
new file mode 100644
index 000000000..967e15af5
--- /dev/null
+++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/MarkdownButton.tsx
@@ -0,0 +1,84 @@
+import '@blocknote/mantine/style.css';
+import {
+ useBlockNoteEditor,
+ useComponentsContext,
+ useSelectedBlocks,
+} from '@blocknote/react';
+import { forEach, isArray } from 'lodash';
+import React, { useMemo } from 'react';
+
+type Block = {
+ type: string;
+ text: string;
+ content: Block[];
+};
+
+function isBlock(block: Block): block is Block {
+ return (
+ block.content &&
+ isArray(block.content) &&
+ block.content.length > 0 &&
+ typeof block.type !== 'undefined'
+ );
+}
+
+const recursiveContent = (content: Block[], base: string = '') => {
+ let fullContent = base;
+ for (const innerContent of content) {
+ if (innerContent.type === 'text') {
+ fullContent += innerContent.text;
+ } else if (isBlock(innerContent)) {
+ fullContent = recursiveContent(innerContent.content, fullContent);
+ }
+ }
+
+ return fullContent;
+};
+
+/**
+ * Custom Formatting Toolbar Button to convert markdown to json.
+ */
+export function MarkdownButton() {
+ const editor = useBlockNoteEditor();
+ const Components = useComponentsContext();
+ const selectedBlocks = useSelectedBlocks(editor);
+
+ const handleConvertMarkdown = () => {
+ const blocks = editor.getSelection()?.blocks;
+
+ forEach(blocks, async (block) => {
+ if (!isBlock(block as unknown as Block)) {
+ return;
+ }
+
+ try {
+ const fullContent = recursiveContent(
+ block.content as unknown as Block[],
+ );
+
+ const blockMarkdown =
+ await editor.tryParseMarkdownToBlocks(fullContent);
+ editor.replaceBlocks([block.id], blockMarkdown);
+ } catch (error) {
+ console.error('Error parsing Markdown:', error);
+ }
+ });
+ };
+
+ const show = useMemo(() => {
+ return !!selectedBlocks.find((block) => block.content !== undefined);
+ }, [selectedBlocks]);
+
+ if (!show || !editor.isEditable || !Components) {
+ return null;
+ }
+
+ return (
+
+ M
+
+ );
+}
diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/PanelEditor.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/PanelEditor.tsx
index 1d81731a0..60ce65b8a 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/PanelEditor.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/PanelEditor.tsx
@@ -3,11 +3,12 @@ import { useTranslation } from 'react-i18next';
import { Box, BoxButton, Card, IconBG, Text } from '@/components';
import { useCunninghamTheme } from '@/cunningham';
-import { Doc } from '@/features/docs//doc-management';
-import { HeadingBlock, TableContent } from '@/features/docs/doc-table-content';
+import { Doc } from '@/features/docs/doc-management';
+import { TableContent } from '@/features/docs/doc-table-content';
import { VersionList } from '@/features/docs/doc-versioning';
-import { usePanelEditorStore } from '../stores/usePanelEditorStore';
+import { usePanelEditorStore } from '../stores';
+import { HeadingBlock } from '../types';
interface PanelProps {
doc: Doc;
@@ -35,7 +36,6 @@ export const PanelEditor = ({
$css={`
top: 0vh;
transform: translateX(0%);
- overflow: hidden;
flex: 1;
margin-left: 1rem;
${
diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/hook/useSaveDoc.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/hook/useSaveDoc.tsx
index 0a21cd45c..ca1ed0054 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-editor/hook/useSaveDoc.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-editor/hook/useSaveDoc.tsx
@@ -4,6 +4,7 @@ import * as Y from 'yjs';
import { useUpdateDoc } from '@/features/docs/doc-management/';
import { KEY_LIST_DOC_VERSIONS } from '@/features/docs/doc-versioning';
+import { isFirefox } from '@/utils/userAgent';
import { toBase64 } from '../utils';
@@ -87,10 +88,7 @@ const useSaveDoc = (docId: string, doc: Y.Doc, canSave: boolean) => {
* if he wants to leave the page, by adding the popup, we let the time to the
* request to be sent, and intercepted by the service worker (for the offline part).
*/
- const isFirefox =
- navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
-
- if (typeof e !== 'undefined' && e.preventDefault && isFirefox) {
+ if (typeof e !== 'undefined' && e.preventDefault && isFirefox()) {
e.preventDefault();
}
};
diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/index.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/index.tsx
index 24ecfc79e..ad4eaebf5 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-editor/index.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-editor/index.tsx
@@ -1,3 +1,4 @@
export * from './components';
export * from './stores';
+export * from './types';
export * from './utils';
diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/stores/index.ts b/src/frontend/apps/impress/src/features/docs/doc-editor/stores/index.ts
index 8efa05869..52c5b5095 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-editor/stores/index.ts
+++ b/src/frontend/apps/impress/src/features/docs/doc-editor/stores/index.ts
@@ -1,2 +1,3 @@
export * from './useDocStore';
+export * from './useHeadingStore';
export * from './usePanelEditorStore';
diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/stores/useDocStore.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/stores/useDocStore.tsx
index ceffcf5fc..ee5750840 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-editor/stores/useDocStore.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-editor/stores/useDocStore.tsx
@@ -6,6 +6,8 @@ import { create } from 'zustand';
import { providerUrl } from '@/core';
import { Base64 } from '@/features/docs/doc-management';
+import { blocksToYDoc } from '../utils';
+
interface DocStore {
provider: HocuspocusProvider;
editor?: BlockNoteEditor;
@@ -28,6 +30,15 @@ export const useDocStore = create((set, get) => ({
if (initialDoc) {
Y.applyUpdate(doc, Buffer.from(initialDoc, 'base64'));
+ } else {
+ const initialDocContent = [
+ {
+ type: 'heading',
+ content: '',
+ },
+ ];
+
+ blocksToYDoc(initialDocContent, doc);
}
const provider = new HocuspocusProvider({
diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/stores/useHeadingStore.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/stores/useHeadingStore.tsx
new file mode 100644
index 000000000..ac9b8a4b3
--- /dev/null
+++ b/src/frontend/apps/impress/src/features/docs/doc-editor/stores/useHeadingStore.tsx
@@ -0,0 +1,43 @@
+import { BlockNoteEditor } from '@blocknote/core';
+import { create } from 'zustand';
+
+import { HeadingBlock } from '../types';
+
+const recursiveTextContent = (content: HeadingBlock['content']): string => {
+ if (!content) {
+ return '';
+ }
+
+ return content.reduce((acc, content) => {
+ if (content.type === 'text') {
+ return acc + content.text;
+ } else if (content.type === 'link') {
+ return acc + recursiveTextContent(content.content);
+ }
+
+ return acc;
+ }, '');
+};
+
+export interface UseHeadingStore {
+ headings: HeadingBlock[];
+ setHeadings: (editor: BlockNoteEditor) => void;
+ resetHeadings: () => void;
+}
+
+export const useHeadingStore = create((set) => ({
+ headings: [],
+ setHeadings: (editor) => {
+ const headingBlocks = editor?.document
+ .filter((block) => block.type === 'heading')
+ .map((block) => ({
+ ...block,
+ contentText: recursiveTextContent(
+ block.content as unknown as HeadingBlock['content'],
+ ),
+ })) as unknown as HeadingBlock[];
+
+ set(() => ({ headings: headingBlocks }));
+ },
+ resetHeadings: () => set(() => ({ headings: [] })),
+}));
diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/types.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/types.tsx
index 4390a173b..19094b6c5 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-editor/types.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-editor/types.tsx
@@ -1,3 +1,14 @@
export interface DocAttachment {
file: string;
}
+
+export type HeadingBlock = {
+ id: string;
+ type: string;
+ text: string;
+ content: HeadingBlock[];
+ contentText: string;
+ props: {
+ level: number;
+ };
+};
diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/utils.ts b/src/frontend/apps/impress/src/features/docs/doc-editor/utils.ts
index aa0d43ba4..2c9495ec9 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-editor/utils.ts
+++ b/src/frontend/apps/impress/src/features/docs/doc-editor/utils.ts
@@ -1,3 +1,5 @@
+import * as Y from 'yjs';
+
export const randomColor = () => {
const randomInt = (min: number, max: number) => {
return Math.floor(Math.random() * (max - min + 1)) + min;
@@ -26,3 +28,20 @@ function hslToHex(h: number, s: number, l: number) {
export const toBase64 = (
str: WithImplicitCoercion,
) => Buffer.from(str).toString('base64');
+
+type BasicBlock = {
+ type: string;
+ content: string;
+};
+export const blocksToYDoc = (blocks: BasicBlock[], doc: Y.Doc) => {
+ const xmlFragment = doc.getXmlFragment('document-store');
+
+ blocks.forEach((block) => {
+ const xmlElement = new Y.XmlElement(block.type);
+ if (block.content) {
+ xmlElement.insert(0, [new Y.XmlText(block.content)]);
+ }
+
+ xmlFragment.push([xmlElement]);
+ });
+};
diff --git a/src/frontend/apps/impress/src/features/docs/doc-header/components/DocHeader.tsx b/src/frontend/apps/impress/src/features/docs/doc-header/components/DocHeader.tsx
index 4f5511098..3c563707b 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-header/components/DocHeader.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-header/components/DocHeader.tsx
@@ -8,12 +8,13 @@ import {
Doc,
Role,
currentDocRole,
- useTransRole,
+ useTrans,
} from '@/features/docs/doc-management';
import { ModalVersion, Versions } from '@/features/docs/doc-versioning';
import { useDate } from '@/hook';
import { DocTagPublic } from './DocTagPublic';
+import { DocTitle } from './DocTitle';
import { DocToolBox } from './DocToolBox';
interface DocHeaderProps {
@@ -25,7 +26,7 @@ export const DocHeader = ({ doc, versionId }: DocHeaderProps) => {
const { colorsTokens } = useCunninghamTheme();
const { t } = useTranslation();
const { formatDate } = useDate();
- const transRole = useTransRole();
+ const { transRole } = useTrans();
const [isModalVersionOpen, setIsModalVersionOpen] = useState(false);
return (
@@ -54,14 +55,8 @@ export const DocHeader = ({ doc, versionId }: DocHeaderProps) => {
$background={colorsTokens()['greyscale-100']}
$margin={{ horizontal: 'small' }}
/>
-
-
- {doc.title}
-
+
+
{versionId && (
- }
- onClose={() => onClose()}
- rightActions={
- validate(title)}
- >
- {buttonText}
-
- }
- size={ModalSize.MEDIUM}
- title={
-
-
-
- {titleModal}
-
-
- }
- >
-
- {infoText && (
-
- {infoText}
-
- )}
-
-
-
-
-
-
- );
-};
diff --git a/src/frontend/apps/impress/src/features/docs/doc-management/components/index.ts b/src/frontend/apps/impress/src/features/docs/doc-management/components/index.ts
index 04d5b0c9c..120ce76ff 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-management/components/index.ts
+++ b/src/frontend/apps/impress/src/features/docs/doc-management/components/index.ts
@@ -1,3 +1,2 @@
-export * from './ModalCreateUpdateDoc';
export * from './ModalRemoveDoc';
export * from './ModalShare';
diff --git a/src/frontend/apps/impress/src/features/docs/doc-management/hooks/index.ts b/src/frontend/apps/impress/src/features/docs/doc-management/hooks/index.ts
index 9a820990c..7697d915a 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-management/hooks/index.ts
+++ b/src/frontend/apps/impress/src/features/docs/doc-management/hooks/index.ts
@@ -1 +1 @@
-export * from './useTransRole';
+export * from './useTrans';
diff --git a/src/frontend/apps/impress/src/features/docs/doc-management/hooks/useTransRole.tsx b/src/frontend/apps/impress/src/features/docs/doc-management/hooks/useTrans.tsx
similarity index 63%
rename from src/frontend/apps/impress/src/features/docs/doc-management/hooks/useTransRole.tsx
rename to src/frontend/apps/impress/src/features/docs/doc-management/hooks/useTrans.tsx
index 337555787..b7bc24f5a 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-management/hooks/useTransRole.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-management/hooks/useTrans.tsx
@@ -2,7 +2,7 @@ import { useTranslation } from 'react-i18next';
import { Role } from '../types';
-export const useTransRole = () => {
+export const useTrans = () => {
const { t } = useTranslation();
const translatedRoles = {
@@ -12,9 +12,10 @@ export const useTransRole = () => {
[Role.EDITOR]: t('Editor'),
};
- const transRole = (role: Role) => {
- return translatedRoles[role];
+ return {
+ transRole: (role: Role) => {
+ return translatedRoles[role];
+ },
+ untitledDocument: t('Untitled document'),
};
-
- return transRole;
};
diff --git a/src/frontend/apps/impress/src/features/docs/doc-table-content/components/TableContent.tsx b/src/frontend/apps/impress/src/features/docs/doc-table-content/components/TableContent.tsx
index b9dde6ead..426058b9d 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-table-content/components/TableContent.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-table-content/components/TableContent.tsx
@@ -2,11 +2,9 @@ import React, { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Box, BoxButton, Text } from '@/components';
-import { useDocStore } from '@/features/docs/doc-editor';
+import { HeadingBlock, useDocStore } from '@/features/docs/doc-editor';
import { Doc } from '@/features/docs/doc-management';
-import { HeadingBlock } from '../types';
-
import { Heading } from './Heading';
interface TableContentProps {
diff --git a/src/frontend/apps/impress/src/features/docs/doc-table-content/hooks/index.ts b/src/frontend/apps/impress/src/features/docs/doc-table-content/hooks/index.ts
deleted file mode 100644
index d9b27305a..000000000
--- a/src/frontend/apps/impress/src/features/docs/doc-table-content/hooks/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export * from './useHeading';
diff --git a/src/frontend/apps/impress/src/features/docs/doc-table-content/hooks/useHeading.tsx b/src/frontend/apps/impress/src/features/docs/doc-table-content/hooks/useHeading.tsx
deleted file mode 100644
index bccc6d659..000000000
--- a/src/frontend/apps/impress/src/features/docs/doc-table-content/hooks/useHeading.tsx
+++ /dev/null
@@ -1,46 +0,0 @@
-import { useCallback, useState } from 'react';
-
-import { useDocStore } from '../../doc-editor';
-import { HeadingBlock } from '../types';
-
-const recursiveTextContent = (content: HeadingBlock['content']): string => {
- if (!content) {
- return '';
- }
-
- return content.reduce((acc, content) => {
- if (content.type === 'text') {
- return acc + content.text;
- } else if (content.type === 'link') {
- return acc + recursiveTextContent(content.content);
- }
-
- return acc;
- }, '');
-};
-
-export const useHeading = (docId: string) => {
- const { docsStore } = useDocStore();
- const editor = docsStore?.[docId]?.editor;
-
- const headingFiltering = useCallback(
- () =>
- editor?.document
- .filter((block) => block.type === 'heading')
- .map((block) => ({
- ...block,
- contentText: recursiveTextContent(
- block.content as unknown as HeadingBlock['content'],
- ),
- })) as unknown as HeadingBlock[],
- [editor?.document],
- );
-
- const [headings, setHeadings] = useState(headingFiltering());
-
- editor?.onEditorContentChange(() => {
- setHeadings(headingFiltering());
- });
-
- return headings;
-};
diff --git a/src/frontend/apps/impress/src/features/docs/doc-table-content/index.ts b/src/frontend/apps/impress/src/features/docs/doc-table-content/index.ts
index 2103701b1..07635cbbc 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-table-content/index.ts
+++ b/src/frontend/apps/impress/src/features/docs/doc-table-content/index.ts
@@ -1,3 +1 @@
export * from './components';
-export * from './hooks';
-export * from './types';
diff --git a/src/frontend/apps/impress/src/features/docs/doc-table-content/types.ts b/src/frontend/apps/impress/src/features/docs/doc-table-content/types.ts
deleted file mode 100644
index feeefc2ec..000000000
--- a/src/frontend/apps/impress/src/features/docs/doc-table-content/types.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-export type HeadingBlock = {
- id: string;
- type: string;
- text: string;
- content: HeadingBlock[];
- contentText: string;
- props: {
- level: number;
- };
-};
diff --git a/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocsGrid.tsx b/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocsGrid.tsx
index cbab72d45..ea755d2e3 100644
--- a/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocsGrid.tsx
+++ b/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocsGrid.tsx
@@ -12,7 +12,7 @@ import {
currentDocRole,
isDocsOrdering,
useDocs,
- useTransRole,
+ useTrans,
} from '@/features/docs/doc-management';
import { useDate } from '@/hook/';
@@ -48,7 +48,7 @@ function formatSortModel(sortModel: SortModelItem): DocsOrdering | undefined {
export const DocsGrid = () => {
const { colorsTokens } = useCunninghamTheme();
- const transRole = useTransRole();
+ const { transRole } = useTrans();
const { t } = useTranslation();
const { formatDate } = useDate();
const pagination = usePagination({
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 d9ae203f5..6a57a2fb0 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
@@ -2,12 +2,7 @@ import { Button } from '@openfun/cunningham-react';
import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
-import { Box, DropButton, IconOptions, Text } from '@/components';
-import {
- Doc,
- ModalRemoveDoc,
- ModalUpdateDoc,
-} from '@/features/docs/doc-management';
+import { Doc, ModalRemoveDoc } from '@/features/docs/doc-management';
interface DocsGridActionsProps {
doc: Doc;
@@ -15,58 +10,24 @@ interface DocsGridActionsProps {
export const DocsGridActions = ({ doc }: DocsGridActionsProps) => {
const { t } = useTranslation();
- const [isModalUpdateOpen, setIsModalUpdateOpen] = useState(false);
const [isModalRemoveOpen, setIsModalRemoveOpen] = useState(false);
- const [isDropOpen, setIsDropOpen] = useState(false);
- if (!doc.abilities.partial_update && !doc.abilities.destroy) {
+ if (!doc.abilities.destroy) {
return null;
}
return (
<>
-
- }
- onOpenChange={(isOpen) => setIsDropOpen(isOpen)}
- isOpen={isDropOpen}
- >
-
- {doc.abilities.partial_update && (
- {
- setIsModalUpdateOpen(true);
- setIsDropOpen(false);
- }}
- color="primary-text"
- icon={edit}
- size="small"
- >
- {t('Update document')}
-
- )}
- {doc.abilities.destroy && (
- {
- setIsModalRemoveOpen(true);
- setIsDropOpen(false);
- }}
- color="primary-text"
- icon={delete}
- size="small"
- >
- {t('Delete document')}
-
- )}
-
-
- {isModalUpdateOpen && (
- setIsModalUpdateOpen(false)} doc={doc} />
- )}
+ {
+ setIsModalRemoveOpen(true);
+ }}
+ color="tertiary-text"
+ icon={delete}
+ size="small"
+ style={{ padding: '0rem' }}
+ aria-label={t('Delete the document')}
+ />
{isModalRemoveOpen && (
setIsModalRemoveOpen(false)} doc={doc} />
)}
diff --git a/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocsGridContainer.tsx b/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocsGridContainer.tsx
index 880f56529..a3534dc2f 100644
--- a/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocsGridContainer.tsx
+++ b/src/frontend/apps/impress/src/features/docs/docs-grid/components/DocsGridContainer.tsx
@@ -1,33 +1,34 @@
import { Button } from '@openfun/cunningham-react';
-import React, { useState } from 'react';
+import { useRouter } from 'next/navigation';
+import React from 'react';
import { useTranslation } from 'react-i18next';
-import { Box, Card } from '@/components';
-import { ModalCreateDoc } from '@/features/docs/doc-management';
+import { Box } from '@/components';
+import { useCreateDoc, useTrans } from '@/features/docs/doc-management/';
import { DocsGrid } from './DocsGrid';
export const DocsGridContainer = () => {
const { t } = useTranslation();
- const [isModalCreateOpen, setIsModalCreateOpen] = useState(false);
+ const { untitledDocument } = useTrans();
+ const router = useRouter();
+
+ const { mutate: createDoc } = useCreateDoc({
+ onSuccess: (doc) => {
+ router.push(`/docs/${doc.id}`);
+ },
+ });
+
+ const handleCreateDoc = () => {
+ createDoc({ title: untitledDocument });
+ };
return (
-
-
- {
- setIsModalCreateOpen(true);
- }}
- >
- {t('Create a new document')}
-
-
-
+
+ {t('Create a new document')}
+
- {isModalCreateOpen && (
- setIsModalCreateOpen(false)} />
- )}
);
};
diff --git a/src/frontend/apps/impress/src/features/docs/members/members-add/components/ChooseRole.tsx b/src/frontend/apps/impress/src/features/docs/members/members-add/components/ChooseRole.tsx
index 7a61fd28f..ba95302fb 100644
--- a/src/frontend/apps/impress/src/features/docs/members/members-add/components/ChooseRole.tsx
+++ b/src/frontend/apps/impress/src/features/docs/members/members-add/components/ChooseRole.tsx
@@ -1,7 +1,7 @@
import { Select } from '@openfun/cunningham-react';
import { useTranslation } from 'react-i18next';
-import { Role, useTransRole } from '@/features/docs/doc-management';
+import { Role, useTrans } from '@/features/docs/doc-management';
interface ChooseRoleProps {
currentRole: Role;
@@ -19,7 +19,7 @@ export const ChooseRole = ({
label,
}: ChooseRoleProps) => {
const { t } = useTranslation();
- const transRole = useTransRole();
+ const { transRole } = useTrans();
return (