✨(frontend) enhance document versioning and loading experience
- Updated tests for document member list and versioning to utilize 'Load more' button instead of mouse wheel scrolling. - Improved UI for document versioning, including visibility checks and modal interactions. - Refactored InfiniteScroll component to include a button for loading more items, enhancing user experience. - Adjusted DocEditor and DocHeader components to handle version IDs more effectively. - Removed deprecated versioning pages to streamline the codebase.
This commit is contained in:
committed by
Anthony LC
parent
5bcce0c64a
commit
a8a89def98
@@ -21,6 +21,9 @@ and this project adheres to
|
||||
- ♻️(frontend) better separation collaboration process #528
|
||||
- 💄(frontend) updating the header and leftpanel for responsive #421
|
||||
- 💄(frontend) update DocsGrid component #431
|
||||
- 💄(frontend) update DocsGridOptions component #432
|
||||
- 💄(frontend) update DocHeader ui #446
|
||||
- 💄(frontend) update doc versioning ui #463
|
||||
|
||||
|
||||
## [1.10.0] - 2024-12-17
|
||||
@@ -40,6 +43,7 @@ and this project adheres to
|
||||
- ⚡️(e2e) reduce flakiness on e2e tests #511
|
||||
|
||||
|
||||
|
||||
## [1.9.0] - 2024-12-11
|
||||
|
||||
## Added
|
||||
@@ -62,8 +66,10 @@ and this project adheres to
|
||||
- 🐛(backend) fix sanitize problem IA #490
|
||||
|
||||
|
||||
|
||||
## [1.8.2] - 2024-11-28
|
||||
|
||||
|
||||
## Changed
|
||||
|
||||
- ♻️(SW) change strategy html caching #460
|
||||
@@ -88,9 +94,6 @@ and this project adheres to
|
||||
- ✨(frontend) config endpoint #424
|
||||
- ✨(frontend) add sentry #424
|
||||
- ✨(frontend) add crisp chatbot #450
|
||||
- 💄(frontend) update DocsGridOptions component #432
|
||||
- 💄(frontend) update DocHeader ui #446
|
||||
|
||||
|
||||
## Changed
|
||||
|
||||
|
||||
@@ -54,8 +54,10 @@ test.describe('Document list members', () => {
|
||||
const list = page.getByLabel('List members card').locator('ul');
|
||||
await expect(list.locator('li')).toHaveCount(20);
|
||||
await list.getByText(`impress@impress.world-page-${1}-18`).hover();
|
||||
await page.mouse.wheel(0, 10);
|
||||
|
||||
const loadMoreButton = page
|
||||
.getByLabel('List members card')
|
||||
.getByRole('button', { name: 'arrow_downward Load more' });
|
||||
await loadMoreButton.scrollIntoViewIfNeeded();
|
||||
await waitForElementCount(list.locator('li'), 21, 10000);
|
||||
|
||||
expect(await list.locator('li').count()).toBeGreaterThan(20);
|
||||
@@ -109,9 +111,13 @@ test.describe('Document list members', () => {
|
||||
await page.getByRole('button', { name: 'Share' }).click();
|
||||
|
||||
const list = page.getByLabel('List invitation card').locator('ul');
|
||||
|
||||
await expect(list.locator('li')).toHaveCount(20);
|
||||
await list.getByText(`impress@impress.world-page-${1}-18`).hover();
|
||||
await page.mouse.wheel(0, 10);
|
||||
const loadMoreButton = page
|
||||
.getByLabel('List invitation card')
|
||||
.getByRole('button', { name: 'arrow_downward Load more' });
|
||||
await loadMoreButton.scrollIntoViewIfNeeded();
|
||||
|
||||
await waitForElementCount(list.locator('li'), 21, 10000);
|
||||
|
||||
|
||||
@@ -23,24 +23,27 @@ test.describe('Doc Version', () => {
|
||||
name: 'Version history',
|
||||
})
|
||||
.click();
|
||||
await expect(page.getByText('History', { exact: true })).toBeVisible();
|
||||
|
||||
const panel = page.getByLabel('Document panel');
|
||||
|
||||
await expect(panel.getByText('Current version')).toBeVisible();
|
||||
expect(await panel.locator('li').count()).toBe(1);
|
||||
const modal = page.getByLabel('version history modal');
|
||||
const panel = modal.getByLabel('version list');
|
||||
await expect(panel).toBeVisible();
|
||||
await expect(modal.getByText('No versions')).toBeVisible();
|
||||
|
||||
await modal.getByRole('button', { name: 'close' }).click();
|
||||
await page.locator('.ProseMirror.bn-editor').click();
|
||||
await page.locator('.ProseMirror.bn-editor').last().fill('Hello World');
|
||||
|
||||
await goToGridDoc(page, {
|
||||
title: randomDoc,
|
||||
});
|
||||
|
||||
await expect(page.getByText('Hello World')).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole('heading', { name: 'Hello World' }),
|
||||
).toBeVisible();
|
||||
|
||||
await page
|
||||
.locator('.ProseMirror .bn-block')
|
||||
.getByText('Hello World')
|
||||
.getByRole('heading', { name: 'Hello World' })
|
||||
.fill('It will create a version');
|
||||
|
||||
await goToGridDoc(page, {
|
||||
@@ -48,7 +51,9 @@ test.describe('Doc Version', () => {
|
||||
});
|
||||
|
||||
await expect(page.getByText('Hello World')).toBeHidden();
|
||||
await expect(page.getByText('It will create a version')).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole('heading', { name: 'It will create a version' }),
|
||||
).toBeVisible();
|
||||
|
||||
await page.getByLabel('Open the document options').click();
|
||||
await page
|
||||
@@ -57,19 +62,16 @@ test.describe('Doc Version', () => {
|
||||
})
|
||||
.click();
|
||||
|
||||
await expect(panel.getByText('Current version')).toBeVisible();
|
||||
expect(await panel.locator('li').count()).toBe(2);
|
||||
await expect(panel).toBeVisible();
|
||||
await expect(page.getByText('History', { exact: true })).toBeVisible();
|
||||
await expect(page.getByRole('status')).toBeVisible();
|
||||
await expect(page.getByRole('status')).toBeHidden();
|
||||
const items = await panel.locator('.version-item').all();
|
||||
expect(items.length).toBe(1);
|
||||
await items[0].click();
|
||||
|
||||
await panel.locator('li').nth(1).click();
|
||||
await expect(
|
||||
page.getByText('Read only, you cannot edit document versions.'),
|
||||
).toBeVisible();
|
||||
await expect(page.getByText('Hello World')).toBeVisible();
|
||||
await expect(page.getByText('It will create a version')).toBeHidden();
|
||||
|
||||
await panel.getByText('Current version').click();
|
||||
await expect(page.getByText('Hello World')).toBeHidden();
|
||||
await expect(page.getByText('It will create a version')).toBeVisible();
|
||||
await expect(modal.getByText('Hello World')).toBeVisible();
|
||||
await expect(modal.getByText('It will create a version')).toBeHidden();
|
||||
});
|
||||
|
||||
test('it does not display the doc versions if not allowed', async ({
|
||||
@@ -90,12 +92,6 @@ test.describe('Doc Version', () => {
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Version history' }),
|
||||
).toBeDisabled();
|
||||
|
||||
await page.getByRole('button', { name: 'Table of content' }).click();
|
||||
|
||||
await expect(
|
||||
page.getByLabel('Document panel').getByText('Versions'),
|
||||
).toBeHidden();
|
||||
});
|
||||
|
||||
test('it restores the doc version', async ({ page, browserName }) => {
|
||||
@@ -105,7 +101,6 @@ test.describe('Doc Version', () => {
|
||||
await page.locator('.bn-block-outer').last().click();
|
||||
await page.locator('.bn-block-outer').last().fill('Hello');
|
||||
|
||||
expect(true).toBe(true);
|
||||
await goToGridDoc(page, {
|
||||
title: randomDoc,
|
||||
});
|
||||
@@ -129,84 +124,26 @@ test.describe('Doc Version', () => {
|
||||
})
|
||||
.click();
|
||||
|
||||
const panel = page.getByLabel('Document panel');
|
||||
await panel.locator('li').nth(1).click();
|
||||
await expect(page.getByText('World')).toBeHidden();
|
||||
const modal = page.getByLabel('version history modal');
|
||||
const panel = modal.getByLabel('version list');
|
||||
await expect(panel).toBeVisible();
|
||||
|
||||
await panel.getByLabel('Open the version options').click();
|
||||
await page.getByText('Restore the version').click();
|
||||
await expect(page.getByText('History', { exact: true })).toBeVisible();
|
||||
await expect(page.getByRole('status')).toBeVisible();
|
||||
await expect(page.getByRole('status')).toBeHidden();
|
||||
const items = await panel.locator('.version-item').all();
|
||||
expect(items.length).toBe(1);
|
||||
await items[0].click();
|
||||
|
||||
await expect(page.getByText('Restore this version?')).toBeVisible();
|
||||
await expect(modal.getByText('World')).toBeHidden();
|
||||
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: 'Restore',
|
||||
})
|
||||
.click();
|
||||
await page.getByRole('button', { name: 'Restore' }).click();
|
||||
await expect(page.getByText('Your current document will')).toBeVisible();
|
||||
await page.getByText('If a member is editing, his').click();
|
||||
|
||||
await expect(panel.locator('li')).toHaveCount(3);
|
||||
await page.getByLabel('Restore', { exact: true }).click();
|
||||
|
||||
await panel.getByText('Current version').click();
|
||||
await expect(page.getByText('Hello')).toBeVisible();
|
||||
await expect(page.getByText('World')).toBeHidden();
|
||||
});
|
||||
|
||||
test('it restores the doc version from button title', async ({
|
||||
page,
|
||||
browserName,
|
||||
}) => {
|
||||
const [randomDoc] = await createDoc(page, 'doc-version', browserName, 1);
|
||||
|
||||
await verifyDocName(page, randomDoc);
|
||||
|
||||
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(editor.getByText('Hello')).toBeVisible();
|
||||
await editor.locator('.bn-block-outer').last().click();
|
||||
await page.keyboard.press('Enter');
|
||||
await editor.locator('.bn-block-outer').last().fill('World');
|
||||
|
||||
await goToGridDoc(page, {
|
||||
title: randomDoc,
|
||||
});
|
||||
|
||||
await expect(editor.getByText('World')).toBeVisible();
|
||||
|
||||
await page.getByLabel('Open the document options').click();
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: 'Version history',
|
||||
})
|
||||
.click();
|
||||
|
||||
const panel = page.getByLabel('Document panel');
|
||||
await panel.locator('li').nth(1).click();
|
||||
await expect(editor.getByText('World')).toBeHidden();
|
||||
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: 'Restore this version',
|
||||
})
|
||||
.click();
|
||||
|
||||
await expect(page.getByText('Restore this version?')).toBeVisible();
|
||||
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: 'Restore',
|
||||
})
|
||||
.click();
|
||||
|
||||
await expect(panel.locator('li')).toHaveCount(3);
|
||||
|
||||
await panel.getByText('Current version').click();
|
||||
await expect(editor.getByText('Hello')).toBeVisible();
|
||||
await expect(editor.getByText('World')).toBeHidden();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -11,6 +11,7 @@ export type DropdownMenuOption = {
|
||||
callback?: () => void | Promise<unknown>;
|
||||
danger?: boolean;
|
||||
disabled?: boolean;
|
||||
show?: boolean;
|
||||
};
|
||||
|
||||
export type DropdownMenuProps = {
|
||||
@@ -59,6 +60,10 @@ export const DropdownMenu = ({
|
||||
>
|
||||
<Box>
|
||||
{options.map((option, index) => {
|
||||
if (option.show !== undefined && !option.show) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isDisabled = option.disabled !== undefined && option.disabled;
|
||||
return (
|
||||
<BoxButton
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
import { PropsWithChildren, useEffect, useRef } from 'react';
|
||||
import { Button } from '@openfun/cunningham-react';
|
||||
import { PropsWithChildren } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { InView } from 'react-intersection-observer';
|
||||
|
||||
import { Box, BoxType } from '@/components';
|
||||
import { Box, BoxType, Icon } from '@/components';
|
||||
|
||||
interface InfiniteScrollProps extends BoxType {
|
||||
hasMore: boolean;
|
||||
isLoading: boolean;
|
||||
next: () => void;
|
||||
scrollContainer: HTMLElement | null;
|
||||
scrollContainer?: HTMLElement | null;
|
||||
buttonLabel?: string;
|
||||
}
|
||||
|
||||
export const InfiniteScroll = ({
|
||||
@@ -14,42 +18,31 @@ export const InfiniteScroll = ({
|
||||
hasMore,
|
||||
isLoading,
|
||||
next,
|
||||
scrollContainer,
|
||||
buttonLabel,
|
||||
...boxProps
|
||||
}: PropsWithChildren<InfiniteScrollProps>) => {
|
||||
const timeout = useRef<ReturnType<typeof setTimeout>>();
|
||||
|
||||
useEffect(() => {
|
||||
if (!scrollContainer) {
|
||||
const { t } = useTranslation();
|
||||
const loadMore = (inView: boolean) => {
|
||||
if (!inView || isLoading) {
|
||||
return;
|
||||
}
|
||||
void next();
|
||||
};
|
||||
|
||||
const nextHandle = () => {
|
||||
if (!hasMore || isLoading) {
|
||||
return;
|
||||
}
|
||||
|
||||
// To not wait until the end of the scroll to load more data
|
||||
const heightFromBottom = 150;
|
||||
|
||||
const { scrollTop, clientHeight, scrollHeight } = scrollContainer;
|
||||
if (scrollTop + clientHeight >= scrollHeight - heightFromBottom) {
|
||||
next();
|
||||
}
|
||||
};
|
||||
|
||||
const handleScroll = () => {
|
||||
if (timeout.current) {
|
||||
clearTimeout(timeout.current);
|
||||
}
|
||||
timeout.current = setTimeout(nextHandle, 50);
|
||||
};
|
||||
|
||||
scrollContainer.addEventListener('scroll', handleScroll);
|
||||
return () => {
|
||||
scrollContainer.removeEventListener('scroll', handleScroll);
|
||||
};
|
||||
}, [hasMore, isLoading, next, scrollContainer]);
|
||||
|
||||
return <Box {...boxProps}>{children}</Box>;
|
||||
return (
|
||||
<Box {...boxProps}>
|
||||
{children}
|
||||
<InView onChange={loadMore}>
|
||||
{!isLoading && hasMore && (
|
||||
<Button
|
||||
onClick={() => void next()}
|
||||
color="primary-text"
|
||||
icon={<Icon iconName="arrow_downward" />}
|
||||
>
|
||||
{buttonLabel ?? t('Load more')}
|
||||
</Button>
|
||||
)}
|
||||
</InView>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -6,7 +6,7 @@ import * as Y from 'yjs';
|
||||
|
||||
import { Box, Card, Text, TextErrors } from '@/components';
|
||||
import { useCunninghamTheme } from '@/cunningham';
|
||||
import { DocHeader } from '@/features/docs/doc-header';
|
||||
import { DocHeader, DocVersionHeader } from '@/features/docs/doc-header/';
|
||||
import {
|
||||
Doc,
|
||||
base64ToBlocknoteXmlFragment,
|
||||
@@ -20,12 +20,10 @@ import { IconOpenPanelEditor, PanelEditor } from './PanelEditor';
|
||||
|
||||
interface DocEditorProps {
|
||||
doc: Doc;
|
||||
versionId?: Versions['version_id'];
|
||||
}
|
||||
|
||||
export const DocEditor = ({ doc }: DocEditorProps) => {
|
||||
const {
|
||||
query: { versionId },
|
||||
} = useRouter();
|
||||
export const DocEditor = ({ doc, versionId }: DocEditorProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { isMobile } = useResponsiveStore();
|
||||
|
||||
@@ -41,7 +39,12 @@ export const DocEditor = ({ doc }: DocEditorProps) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<DocHeader doc={doc} />
|
||||
{isVersion ? (
|
||||
<DocVersionHeader title={doc.title} />
|
||||
) : (
|
||||
<DocHeader doc={doc} />
|
||||
)}
|
||||
|
||||
{!doc.abilities.partial_update && (
|
||||
<Box $width="100%" $margin={{ all: 'small', top: 'none' }}>
|
||||
<Alert type={VariantType.WARNING}>
|
||||
@@ -49,18 +52,11 @@ export const DocEditor = ({ doc }: DocEditorProps) => {
|
||||
</Alert>
|
||||
</Box>
|
||||
)}
|
||||
{isVersion && (
|
||||
<Box $margin={{ all: 'small', top: 'none' }}>
|
||||
<Alert type={VariantType.WARNING}>
|
||||
{t(`Read only, you cannot edit document versions.`)}
|
||||
</Alert>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
<Box
|
||||
$background={colorsTokens()['primary-bg']}
|
||||
$direction="row"
|
||||
$width="100%"
|
||||
$margin={{ all: isMobile ? 'tiny' : 'small', top: 'none' }}
|
||||
$css="overflow-x: clip; flex: 1;"
|
||||
$position="relative"
|
||||
>
|
||||
@@ -75,9 +71,9 @@ export const DocEditor = ({ doc }: DocEditorProps) => {
|
||||
) : (
|
||||
<BlockNoteEditor doc={doc} provider={provider} />
|
||||
)}
|
||||
{!isMobile && <IconOpenPanelEditor />}
|
||||
{!isMobile && !isVersion && <IconOpenPanelEditor />}
|
||||
</Card>
|
||||
<PanelEditor doc={doc} />
|
||||
{!isVersion && <PanelEditor />}
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,20 +1,14 @@
|
||||
import React, { PropsWithChildren, useEffect, useState } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
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 { TableContent } from '@/features/docs/doc-table-content';
|
||||
import { VersionList } from '@/features/docs/doc-versioning';
|
||||
import { useResponsiveStore } from '@/stores';
|
||||
|
||||
import { useHeadingStore, usePanelEditorStore } from '../stores';
|
||||
|
||||
interface PanelProps {
|
||||
doc: Doc;
|
||||
}
|
||||
|
||||
export const PanelEditor = ({ doc }: PropsWithChildren<PanelProps>) => {
|
||||
export const PanelEditor = () => {
|
||||
const { t } = useTranslation();
|
||||
const { colorsTokens } = useCunninghamTheme();
|
||||
const { isMobile } = useResponsiveStore();
|
||||
@@ -72,7 +66,7 @@ export const PanelEditor = ({ doc }: PropsWithChildren<PanelProps>) => {
|
||||
$background="white"
|
||||
$position="absolute"
|
||||
$height="100%"
|
||||
$width={doc.abilities.versions_list ? '50%' : '100%'}
|
||||
$width="100%"
|
||||
$hasTransition="slow"
|
||||
$css={`
|
||||
border-top: 2px solid ${colorsTokens()['primary-600']};
|
||||
@@ -88,7 +82,7 @@ export const PanelEditor = ({ doc }: PropsWithChildren<PanelProps>) => {
|
||||
`}
|
||||
/>
|
||||
<BoxButton
|
||||
$minWidth={doc.abilities.versions_list ? '50%' : '100%'}
|
||||
$minWidth="100%"
|
||||
onClick={() => setIsPanelTableContentOpen(true)}
|
||||
$zIndex={1}
|
||||
>
|
||||
@@ -103,29 +97,8 @@ export const PanelEditor = ({ doc }: PropsWithChildren<PanelProps>) => {
|
||||
{t('Table of content')}
|
||||
</Text>
|
||||
</BoxButton>
|
||||
{doc.abilities.versions_list && (
|
||||
<BoxButton
|
||||
$minWidth="50%"
|
||||
onClick={() => setIsPanelTableContentOpen(false)}
|
||||
$zIndex={1}
|
||||
>
|
||||
<Text
|
||||
$width="100%"
|
||||
$weight="bold"
|
||||
$size="m"
|
||||
$theme="primary"
|
||||
$variation="600"
|
||||
$padding={{ vertical: 'small', horizontal: 'small' }}
|
||||
>
|
||||
{t('Versions')}
|
||||
</Text>
|
||||
</BoxButton>
|
||||
)}
|
||||
</Box>
|
||||
{isPanelTableContentOpen && <TableContent />}
|
||||
{!isPanelTableContentOpen && doc.abilities.versions_list && (
|
||||
<VersionList doc={doc} />
|
||||
)}
|
||||
<TableContent />
|
||||
</Box>
|
||||
</Card>
|
||||
);
|
||||
|
||||
@@ -68,6 +68,7 @@ export const DocHeader = ({ doc }: DocHeaderProps) => {
|
||||
>
|
||||
<Box $gap={spacings['3xs']}>
|
||||
<DocTitle doc={doc} />
|
||||
|
||||
<Box $direction="row">
|
||||
{isDesktop && (
|
||||
<>
|
||||
|
||||
@@ -25,23 +25,30 @@ interface DocTitleProps {
|
||||
}
|
||||
|
||||
export const DocTitle = ({ doc }: DocTitleProps) => {
|
||||
const { isMobile } = useResponsiveStore();
|
||||
|
||||
if (!doc.abilities.partial_update) {
|
||||
return (
|
||||
<Text
|
||||
as="h2"
|
||||
$margin={{ all: 'none', left: 'none' }}
|
||||
$size={isMobile ? 'h4' : 'h2'}
|
||||
>
|
||||
{doc.title}
|
||||
</Text>
|
||||
);
|
||||
return <DocTitleText title={doc.title} />;
|
||||
}
|
||||
|
||||
return <DocTitleInput doc={doc} />;
|
||||
};
|
||||
|
||||
interface DocTitleTextProps {
|
||||
title: string;
|
||||
}
|
||||
|
||||
export const DocTitleText = ({ title }: DocTitleTextProps) => {
|
||||
const { isMobile } = useResponsiveStore();
|
||||
return (
|
||||
<Text
|
||||
as="h2"
|
||||
$margin={{ all: 'none', left: 'none' }}
|
||||
$size={isMobile ? 'h4' : 'h2'}
|
||||
>
|
||||
{title}
|
||||
</Text>
|
||||
);
|
||||
};
|
||||
|
||||
const DocTitleInput = ({ doc }: DocTitleProps) => {
|
||||
const { isDesktop } = useResponsiveStore();
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import {
|
||||
Button,
|
||||
VariantType,
|
||||
useModal,
|
||||
useToastProvider,
|
||||
} from '@openfun/cunningham-react';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { css } from 'styled-components';
|
||||
@@ -26,7 +26,7 @@ import {
|
||||
ModalRemoveDoc,
|
||||
ModalShare,
|
||||
} from '@/features/docs/doc-management';
|
||||
import { ModalVersion } from '@/features/docs/doc-versioning';
|
||||
import { ModalSelectVersion } from '@/features/docs/doc-versioning';
|
||||
import { useResponsiveStore } from '@/stores';
|
||||
|
||||
import { ModalPDF } from './ModalExport';
|
||||
@@ -36,9 +36,6 @@ interface DocToolBoxProps {
|
||||
}
|
||||
|
||||
export const DocToolBox = ({ doc }: DocToolBoxProps) => {
|
||||
const {
|
||||
query: { versionId },
|
||||
} = useRouter();
|
||||
const { t } = useTranslation();
|
||||
const { spacingsTokens, colorsTokens } = useCunninghamTheme();
|
||||
|
||||
@@ -48,10 +45,10 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => {
|
||||
const [isModalShareOpen, setIsModalShareOpen] = useState(false);
|
||||
const [isModalRemoveOpen, setIsModalRemoveOpen] = useState(false);
|
||||
const [isModalPDFOpen, setIsModalPDFOpen] = useState(false);
|
||||
|
||||
const selectHistoryModal = useModal();
|
||||
const { setIsPanelOpen, setIsPanelTableContentOpen } = usePanelEditorStore();
|
||||
const [isModalVersionOpen, setIsModalVersionOpen] = useState(false);
|
||||
const { isSmallMobile } = useResponsiveStore();
|
||||
|
||||
const { isSmallMobile, isDesktop } = useResponsiveStore();
|
||||
const { authenticated } = useAuthStore();
|
||||
const { editor } = useEditorStore();
|
||||
const { toast } = useToastProvider();
|
||||
@@ -80,9 +77,9 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => {
|
||||
icon: 'history',
|
||||
disabled: !doc.abilities.versions_list,
|
||||
callback: () => {
|
||||
setIsPanelOpen(true);
|
||||
setIsPanelTableContentOpen(false);
|
||||
selectHistoryModal.open();
|
||||
},
|
||||
show: isDesktop,
|
||||
},
|
||||
{
|
||||
label: t('Table of contents'),
|
||||
@@ -147,19 +144,6 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => {
|
||||
$gap="0.5rem 1.5rem"
|
||||
$wrap={isSmallMobile ? 'wrap' : 'nowrap'}
|
||||
>
|
||||
{versionId && (
|
||||
<Box $margin={{ left: 'auto' }}>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setIsModalVersionOpen(true);
|
||||
}}
|
||||
color="secondary"
|
||||
size={isSmallMobile ? 'small' : 'medium'}
|
||||
>
|
||||
{t('Restore this version')}
|
||||
</Button>
|
||||
</Box>
|
||||
)}
|
||||
<Box $direction="row" $margin={{ left: 'auto' }} $gap={spacings['2xs']}>
|
||||
{authenticated && !isSmallMobile && (
|
||||
<Button
|
||||
@@ -210,11 +194,10 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => {
|
||||
{isModalRemoveOpen && (
|
||||
<ModalRemoveDoc onClose={() => setIsModalRemoveOpen(false)} doc={doc} />
|
||||
)}
|
||||
{isModalVersionOpen && versionId && (
|
||||
<ModalVersion
|
||||
onClose={() => setIsModalVersionOpen(false)}
|
||||
docId={doc.id}
|
||||
versionId={versionId as string}
|
||||
{selectHistoryModal.isOpen && (
|
||||
<ModalSelectVersion
|
||||
onClose={() => selectHistoryModal.close()}
|
||||
doc={doc}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { Box } from '@/components';
|
||||
import { HorizontalSeparator } from '@/components/separators/HorizontalSeparator';
|
||||
import { useCunninghamTheme } from '@/cunningham';
|
||||
|
||||
import { DocTitleText } from './DocTitle';
|
||||
|
||||
interface DocVersionHeaderProps {
|
||||
title: string;
|
||||
}
|
||||
|
||||
export const DocVersionHeader = ({ title }: DocVersionHeaderProps) => {
|
||||
const { spacingsTokens } = useCunninghamTheme();
|
||||
|
||||
const spacings = spacingsTokens();
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box
|
||||
$width="100%"
|
||||
$padding={{ vertical: 'base' }}
|
||||
$gap={spacings['base']}
|
||||
aria-label={t('It is the document title')}
|
||||
>
|
||||
<DocTitleText title={title} />
|
||||
<HorizontalSeparator />
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -1 +1,2 @@
|
||||
export * from './DocHeader';
|
||||
export * from './DocVersionHeader';
|
||||
|
||||
@@ -77,6 +77,7 @@ export function useDocVersionsInfiniteQuery(
|
||||
getNextPageParam(lastPage) {
|
||||
return lastPage.next_version_id_marker || undefined;
|
||||
},
|
||||
|
||||
...queryConfig,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import {
|
||||
Alert,
|
||||
Button,
|
||||
Modal,
|
||||
ModalSize,
|
||||
@@ -22,17 +21,18 @@ import { KEY_LIST_DOC_VERSIONS } from '../api/useDocVersions';
|
||||
import { Versions } from '../types';
|
||||
import { revertUpdate } from '../utils';
|
||||
|
||||
interface ModalVersionProps {
|
||||
interface ModalConfirmationVersionProps {
|
||||
onClose: () => void;
|
||||
docId: Doc['id'];
|
||||
|
||||
versionId: Versions['version_id'];
|
||||
}
|
||||
|
||||
export const ModalVersion = ({
|
||||
export const ModalConfirmationVersion = ({
|
||||
onClose,
|
||||
docId,
|
||||
versionId,
|
||||
}: ModalVersionProps) => {
|
||||
}: ModalConfirmationVersionProps) => {
|
||||
const { data: version } = useDocVersion({
|
||||
docId,
|
||||
versionId,
|
||||
@@ -68,60 +68,50 @@ export const ModalVersion = ({
|
||||
<Modal
|
||||
isOpen
|
||||
closeOnClickOutside
|
||||
hideCloseButton
|
||||
leftActions={
|
||||
<Button
|
||||
aria-label={t('Close the modal')}
|
||||
color="secondary"
|
||||
fullWidth
|
||||
onClick={() => onClose()}
|
||||
>
|
||||
{t('Cancel')}
|
||||
</Button>
|
||||
}
|
||||
onClose={() => onClose()}
|
||||
rightActions={
|
||||
<Button
|
||||
aria-label={t('Restore')}
|
||||
color="primary"
|
||||
fullWidth
|
||||
onClick={() => {
|
||||
if (!version?.content) {
|
||||
return;
|
||||
}
|
||||
<>
|
||||
<Button
|
||||
aria-label={t('Close the modal')}
|
||||
color="secondary"
|
||||
fullWidth
|
||||
onClick={() => onClose()}
|
||||
>
|
||||
{t('Cancel')}
|
||||
</Button>
|
||||
<Button
|
||||
aria-label={t('Restore')}
|
||||
color="danger"
|
||||
fullWidth
|
||||
onClick={() => {
|
||||
if (!version?.content) {
|
||||
return;
|
||||
}
|
||||
|
||||
updateDoc({
|
||||
id: docId,
|
||||
content: version.content,
|
||||
});
|
||||
updateDoc({
|
||||
id: docId,
|
||||
content: version.content,
|
||||
});
|
||||
|
||||
onClose();
|
||||
}}
|
||||
>
|
||||
{t('Restore')}
|
||||
</Button>
|
||||
onClose();
|
||||
}}
|
||||
>
|
||||
{t('Restore')}
|
||||
</Button>
|
||||
</>
|
||||
}
|
||||
size={ModalSize.MEDIUM}
|
||||
title={
|
||||
<Box $gap="1rem">
|
||||
<Text $isMaterialIcon $size="36px" $theme="primary">
|
||||
restore
|
||||
</Text>
|
||||
<Text as="h2" $size="h3" $margin="none">
|
||||
{t('Restore this version?')}
|
||||
</Text>
|
||||
</Box>
|
||||
<Text $size="h6" $align="flex-start">
|
||||
{t('Warning')}
|
||||
</Text>
|
||||
}
|
||||
>
|
||||
<Box aria-label={t('Modal confirmation to restore the version')}>
|
||||
<Alert canClose={false} type={VariantType.WARNING}>
|
||||
<Box>
|
||||
<Text>
|
||||
{t('Your current document will revert to this version.')}
|
||||
</Text>
|
||||
<Text>{t('If a member is editing, his works can be lost.')}</Text>
|
||||
</Box>
|
||||
</Alert>
|
||||
<Box>
|
||||
<Text>{t('Your current document will revert to this version.')}</Text>
|
||||
<Text>{t('If a member is editing, his works can be lost.')}</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
</Modal>
|
||||
);
|
||||
@@ -0,0 +1,156 @@
|
||||
import { Button, Modal, ModalSize, useModal } from '@openfun/cunningham-react';
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { createGlobalStyle, css } from 'styled-components';
|
||||
|
||||
import { Box, Icon, Text } from '@/components';
|
||||
|
||||
import { DocEditor } from '../../doc-editor/components/DocEditor';
|
||||
import { Doc } from '../../doc-management';
|
||||
import { Versions } from '../types';
|
||||
|
||||
import { ModalConfirmationVersion } from './ModalConfirmationVersion';
|
||||
import { VersionList } from './VersionList';
|
||||
|
||||
const NoPaddingStyle = createGlobalStyle`
|
||||
.c__modal__scroller:has(.noPadding) {
|
||||
padding: 0 !important;
|
||||
|
||||
.c__modal__close .c__button {
|
||||
right: 0;
|
||||
top: 7px;
|
||||
padding: 1rem 0.5rem;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
type ModalSelectVersionProps = {
|
||||
doc: Doc;
|
||||
onClose: () => void;
|
||||
};
|
||||
|
||||
export const ModalSelectVersion = ({
|
||||
onClose,
|
||||
doc,
|
||||
}: ModalSelectVersionProps) => {
|
||||
const { t } = useTranslation();
|
||||
const [selectedVersionId, setSelectedVersionId] =
|
||||
useState<Versions['version_id']>();
|
||||
|
||||
const restoreModal = useModal();
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
isOpen
|
||||
hideCloseButton
|
||||
closeOnClickOutside={true}
|
||||
size={ModalSize.EXTRA_LARGE}
|
||||
onClose={onClose}
|
||||
>
|
||||
<NoPaddingStyle />
|
||||
<Box
|
||||
aria-label="version history modal"
|
||||
className="noPadding"
|
||||
$direction="row"
|
||||
$height="calc(100vh - 50px);"
|
||||
$overflow="hidden"
|
||||
>
|
||||
<Box
|
||||
$css={css`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
`}
|
||||
>
|
||||
<Box $width="100%" $padding="base">
|
||||
{selectedVersionId && (
|
||||
<DocEditor doc={doc} versionId={selectedVersionId} />
|
||||
)}
|
||||
{!selectedVersionId && (
|
||||
<Box $align="center" $justify="center" $height="100%">
|
||||
<Text $size="h6" $weight="bold">
|
||||
{t('Select a version on the right to restore')}
|
||||
</Text>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
<Box
|
||||
$direction="column"
|
||||
$justify="space-between"
|
||||
$width="250px"
|
||||
$height="calc(100vh - 2em - 30px);"
|
||||
$css={css`
|
||||
overflow-y: hidden;
|
||||
border-left: 1px solid var(--c--theme--colors--greyscale-200);
|
||||
`}
|
||||
>
|
||||
<Box
|
||||
aria-label="version list"
|
||||
$css={css`
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
`}
|
||||
>
|
||||
<Box
|
||||
$width="100%"
|
||||
$justify="space-between"
|
||||
$direction="row"
|
||||
$align="center"
|
||||
$css={css`
|
||||
border-bottom: 1px solid
|
||||
var(--c--theme--colors--greyscale-200);
|
||||
`}
|
||||
$padding="sm"
|
||||
>
|
||||
<Text $size="h6" $weight="bold">
|
||||
{t('History')}
|
||||
</Text>
|
||||
<Button
|
||||
onClick={onClose}
|
||||
size="nano"
|
||||
color="primary-text"
|
||||
icon={<Icon iconName="close" />}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<VersionList
|
||||
doc={doc}
|
||||
onSelectVersion={setSelectedVersionId}
|
||||
selectedVersionId={selectedVersionId}
|
||||
/>
|
||||
</Box>
|
||||
<Box
|
||||
$padding="base"
|
||||
$css={css`
|
||||
border-top: 1px solid var(--c--theme--colors--greyscale-200);
|
||||
`}
|
||||
>
|
||||
<Button
|
||||
fullWidth
|
||||
disabled={!selectedVersionId}
|
||||
onClick={restoreModal.open}
|
||||
color="primary"
|
||||
>
|
||||
{t('Restore')}
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</Modal>
|
||||
{restoreModal.isOpen && selectedVersionId && (
|
||||
<ModalConfirmationVersion
|
||||
onClose={() => {
|
||||
restoreModal.close();
|
||||
onClose();
|
||||
setSelectedVersionId(undefined);
|
||||
}}
|
||||
docId={doc.id}
|
||||
versionId={selectedVersionId}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -1,19 +1,17 @@
|
||||
import { Button } from '@openfun/cunningham-react';
|
||||
import { PropsWithChildren, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { Box, DropButton, IconOptions, StyledLink, Text } from '@/components';
|
||||
import { Box, Text } from '@/components';
|
||||
import { useCunninghamTheme } from '@/cunningham';
|
||||
import { Doc } from '@/features/docs/doc-management';
|
||||
|
||||
import { Versions } from '../types';
|
||||
|
||||
import { ModalVersion } from './ModalVersion';
|
||||
import { ModalConfirmationVersion } from './ModalConfirmationVersion';
|
||||
|
||||
interface VersionItemProps {
|
||||
docId: Doc['id'];
|
||||
text: string;
|
||||
link: string;
|
||||
|
||||
versionId?: Versions['version_id'];
|
||||
isActive: boolean;
|
||||
}
|
||||
@@ -22,78 +20,47 @@ export const VersionItem = ({
|
||||
docId,
|
||||
versionId,
|
||||
text,
|
||||
link,
|
||||
|
||||
isActive,
|
||||
}: VersionItemProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { colorsTokens } = useCunninghamTheme();
|
||||
const [isDropOpen, setIsDropOpen] = useState(false);
|
||||
const { colorsTokens, spacingsTokens } = useCunninghamTheme();
|
||||
const spacing = spacingsTokens();
|
||||
|
||||
const [isModalVersionOpen, setIsModalVersionOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box
|
||||
$width="100%"
|
||||
as="li"
|
||||
$background={isActive ? colorsTokens()['primary-300'] : 'transparent'}
|
||||
$background={isActive ? colorsTokens()['greyscale-100'] : 'transparent'}
|
||||
$radius={spacing['3xs']}
|
||||
$css={`
|
||||
border-left: 4px solid transparent;
|
||||
border-bottom: 1px solid ${colorsTokens()['primary-100']};
|
||||
&:hover{
|
||||
border-left: 4px solid ${colorsTokens()['primary-400']};
|
||||
background: ${colorsTokens()['primary-300']};
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: ${colorsTokens()['greyscale-100']};
|
||||
}
|
||||
`}
|
||||
$hasTransition
|
||||
$minWidth="13rem"
|
||||
>
|
||||
<Link href={link} isActive={isActive}>
|
||||
<Box
|
||||
$padding={{ vertical: '0.7rem', horizontal: 'small' }}
|
||||
$align="center"
|
||||
$direction="row"
|
||||
$justify="space-between"
|
||||
$width="100%"
|
||||
>
|
||||
<Box $direction="row" $gap="0.5rem" $align="center">
|
||||
<Text
|
||||
$isMaterialIcon
|
||||
$size="24px"
|
||||
$theme="primary"
|
||||
$variation="600"
|
||||
>
|
||||
description
|
||||
</Text>
|
||||
<Text $weight="bold" $theme="primary" $size="m" $variation="600">
|
||||
{text}
|
||||
</Text>
|
||||
</Box>
|
||||
{isActive && versionId && (
|
||||
<DropButton
|
||||
button={
|
||||
<IconOptions aria-label={t('Open the version options')} />
|
||||
}
|
||||
onOpenChange={(isOpen) => setIsDropOpen(isOpen)}
|
||||
isOpen={isDropOpen}
|
||||
>
|
||||
<Box>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setIsModalVersionOpen(true);
|
||||
}}
|
||||
color="primary-text"
|
||||
icon={<span className="material-icons">save</span>}
|
||||
size="small"
|
||||
>
|
||||
{t('Restore the version')}
|
||||
</Button>
|
||||
</Box>
|
||||
</DropButton>
|
||||
)}
|
||||
<Box
|
||||
$padding={{ vertical: '0.7rem', horizontal: 'small' }}
|
||||
$align="center"
|
||||
$direction="row"
|
||||
$justify="space-between"
|
||||
$width="100%"
|
||||
>
|
||||
<Box $direction="row" $gap="0.5rem" $align="center">
|
||||
<Text $weight="bold" $size="sm" $variation="1000">
|
||||
{text}
|
||||
</Text>
|
||||
</Box>
|
||||
</Link>
|
||||
</Box>
|
||||
</Box>
|
||||
{isModalVersionOpen && versionId && (
|
||||
<ModalVersion
|
||||
<ModalConfirmationVersion
|
||||
onClose={() => setIsModalVersionOpen(false)}
|
||||
docId={docId}
|
||||
versionId={versionId}
|
||||
@@ -102,16 +69,3 @@ export const VersionItem = ({
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
interface LinkProps {
|
||||
href: string;
|
||||
isActive: boolean;
|
||||
}
|
||||
|
||||
const Link = ({ href, children, isActive }: PropsWithChildren<LinkProps>) => {
|
||||
return isActive ? (
|
||||
<>{children}</>
|
||||
) : (
|
||||
<StyledLink href={href}>{children}</StyledLink>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Loader } from '@openfun/cunningham-react';
|
||||
import { useRouter } from 'next/router';
|
||||
import React, { useMemo, useRef } from 'react';
|
||||
import { DateTime } from 'luxon';
|
||||
import { useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { APIError } from '@/api';
|
||||
import { Box, InfiniteScroll, Text, TextErrors } from '@/components';
|
||||
import { Box, BoxButton, InfiniteScroll, Text, TextErrors } from '@/components';
|
||||
import { Doc } from '@/features/docs/doc-management';
|
||||
import { useDate } from '@/hook';
|
||||
|
||||
@@ -18,19 +18,20 @@ interface VersionListStateProps {
|
||||
error: APIError<unknown> | null;
|
||||
versions?: Versions[];
|
||||
doc: Doc;
|
||||
selectedVersionId?: Versions['version_id'];
|
||||
onSelectVersion?: (versionId: Versions['version_id']) => void;
|
||||
}
|
||||
|
||||
const VersionListState = ({
|
||||
onSelectVersion,
|
||||
selectedVersionId,
|
||||
|
||||
isLoading,
|
||||
error,
|
||||
versions,
|
||||
doc,
|
||||
}: VersionListStateProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { formatDate } = useDate();
|
||||
const {
|
||||
query: { versionId },
|
||||
} = useRouter();
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
@@ -41,26 +42,23 @@ const VersionListState = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<VersionItem
|
||||
text={t('Current version')}
|
||||
versionId={undefined}
|
||||
link={`/docs/${doc.id}/`}
|
||||
docId={doc.id}
|
||||
isActive={!versionId}
|
||||
/>
|
||||
<Box $gap="10px" $padding="xs">
|
||||
{versions?.map((version) => (
|
||||
<VersionItem
|
||||
<BoxButton
|
||||
aria-label="version item"
|
||||
className="version-item"
|
||||
key={version.version_id}
|
||||
versionId={version.version_id}
|
||||
text={formatDate(version.last_modified, {
|
||||
dateStyle: 'long',
|
||||
timeStyle: 'short',
|
||||
})}
|
||||
link={`/docs/${doc.id}/versions/${version.version_id}`}
|
||||
docId={doc.id}
|
||||
isActive={version.version_id === versionId}
|
||||
/>
|
||||
onClick={() => {
|
||||
onSelectVersion?.(version.version_id);
|
||||
}}
|
||||
>
|
||||
<VersionItem
|
||||
versionId={version.version_id}
|
||||
text={formatDate(version.last_modified, DateTime.DATETIME_MED)}
|
||||
docId={doc.id}
|
||||
isActive={version.version_id === selectedVersionId}
|
||||
/>
|
||||
</BoxButton>
|
||||
))}
|
||||
{error && (
|
||||
<Box
|
||||
@@ -79,15 +77,23 @@ const VersionListState = ({
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
</>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
interface VersionListProps {
|
||||
doc: Doc;
|
||||
onSelectVersion?: (versionId: Versions['version_id']) => void;
|
||||
selectedVersionId?: Versions['version_id'];
|
||||
}
|
||||
|
||||
export const VersionList = ({ doc }: VersionListProps) => {
|
||||
export const VersionList = ({
|
||||
doc,
|
||||
onSelectVersion,
|
||||
selectedVersionId,
|
||||
}: VersionListProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const {
|
||||
data,
|
||||
error,
|
||||
@@ -98,7 +104,7 @@ export const VersionList = ({ doc }: VersionListProps) => {
|
||||
} = useDocVersionsInfiniteQuery({
|
||||
docId: doc.id,
|
||||
});
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const versions = useMemo(() => {
|
||||
return data?.pages.reduce((acc, page) => {
|
||||
return acc.concat(page.versions);
|
||||
@@ -106,24 +112,32 @@ export const VersionList = ({ doc }: VersionListProps) => {
|
||||
}, [data?.pages]);
|
||||
|
||||
return (
|
||||
<Box $css="overflow-y: auto; overflow-x: hidden;" ref={containerRef}>
|
||||
<Box $css="overflow-y: auto; overflow-x: hidden;">
|
||||
<InfiniteScroll
|
||||
hasMore={hasNextPage}
|
||||
isLoading={isFetchingNextPage}
|
||||
next={() => {
|
||||
void fetchNextPage();
|
||||
}}
|
||||
scrollContainer={containerRef.current}
|
||||
as="ul"
|
||||
$padding="none"
|
||||
$margin={{ top: 'none' }}
|
||||
role="listbox"
|
||||
>
|
||||
{versions?.length === 0 && (
|
||||
<Box $align="center" $margin="large">
|
||||
<Text $size="h6" $weight="bold">
|
||||
{t('No versions')}
|
||||
</Text>
|
||||
</Box>
|
||||
)}
|
||||
<VersionListState
|
||||
onSelectVersion={onSelectVersion}
|
||||
isLoading={isLoading}
|
||||
error={error}
|
||||
versions={versions}
|
||||
doc={doc}
|
||||
selectedVersionId={selectedVersionId}
|
||||
/>
|
||||
</InfiniteScroll>
|
||||
</Box>
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
export * from './ModalVersion';
|
||||
export * from './ModalConfirmationVersion';
|
||||
export * from './ModalSelectVersion';
|
||||
export * from './VersionList';
|
||||
|
||||
@@ -19,7 +19,7 @@ export const DocsGridItem = ({ doc }: DocsGridItemProps) => {
|
||||
const isPublic = doc.link_reach === LinkReach.PUBLIC;
|
||||
const isAuthenticated = doc.link_reach === LinkReach.AUTHENTICATED;
|
||||
const isRestricted = doc.link_reach === LinkReach.RESTRICTED;
|
||||
const sharedCount = doc.accesses.length - 1;
|
||||
const sharedCount = doc.nb_accesses - 1;
|
||||
const isShared = sharedCount > 0;
|
||||
|
||||
return (
|
||||
|
||||
@@ -33,8 +33,8 @@ export const SimpleDocItem = ({
|
||||
const spacings = spacingsTokens();
|
||||
|
||||
const isPublic = doc?.link_reach === LinkReach.PUBLIC;
|
||||
const isShared = !isPublic && doc.accesses.length > 1;
|
||||
const accessCount = doc.accesses.length - 1;
|
||||
const isShared = !isPublic && doc.nb_accesses > 1;
|
||||
const accessCount = doc.nb_accesses - 1;
|
||||
const isSharedOrPublic = isShared || isPublic;
|
||||
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user