Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ and this project adheres to
- ♿️(frontend) announce formatting shortcuts for screen readers #2070
- ✨(frontend) add markdown copy icon for Copy as Markdown option #2096
- ♻️(backend) skip saving in database a document when payload is empty #2062
- ♻️(frontend) refacto Version modal to fit with the design system #2091

### Fixed

Expand All @@ -22,7 +23,7 @@ and this project adheres to
- 💫(frontend) fix the help button to the bottom in tree #2073
- ♿️(frontend) fix aria-labels for table of contents #2065
- 🐛(backend) allow using search endpoint without refresh token enabled #2097
- 🐛(frontend) fix close panel when click on subdoc
- 🐛(frontend) fix close panel when click on subdoc #2094
- 🐛(frontend) fix leftpanel button in doc version #9238

## [v4.8.2] - 2026-03-19
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,11 +164,16 @@ test.describe('Doc Version', () => {
await expect(modal.getByText('World')).toBeHidden();

await page.getByRole('button', { name: 'Restore', exact: true }).click();
await expect(page.getByText('Your current document will')).toBeVisible();
await page.getByText('If a member is editing, his').click();
await expect(
page.getByText(
"The current document will be replaced, but you'll still find it in the version history.",
),
).toBeVisible();

await page.getByLabel('Restore', { exact: true }).click();

await page.waitForTimeout(500);

await expect(page.getByText('Hello')).toBeVisible();
await expect(page.getByText('World')).toBeHidden();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import {
VariantType,
useToastProvider,
} from '@gouvfr-lasuite/cunningham-react';
import { useRouter } from 'next/router';
import { useTranslation } from 'react-i18next';
import { createGlobalStyle } from 'styled-components';

import { Box, Text } from '@/components';
import {
Expand All @@ -21,15 +21,22 @@ import { KEY_LIST_DOC_VERSIONS } from '../api/useDocVersions';
import { Versions } from '../types';
import { revertUpdate } from '../utils';

const ModalStyle = createGlobalStyle`
.c__modal__title {
margin-bottom: var(--c--globals--spacings--sm);
}
`;

interface ModalConfirmationVersionProps {
onClose: () => void;
docId: Doc['id'];

onClose: () => void;
onSuccess: () => void;
versionId: Versions['version_id'];
}

export const ModalConfirmationVersion = ({
onClose,
onSuccess,
docId,
versionId,
}: ModalConfirmationVersionProps) => {
Expand All @@ -39,14 +46,13 @@ export const ModalConfirmationVersion = ({
});
const { t } = useTranslation();
const { toast } = useToastProvider();
const { push } = useRouter();
const { provider } = useProviderStore();
const { mutate: updateDoc } = useUpdateDoc({
listInvalidQueries: [KEY_LIST_DOC_VERSIONS],
onSuccess: () => {
const onDisplaySuccess = () => {
toast(t('Version restored successfully'), VariantType.SUCCESS);
void push(`/docs/${docId}`);
onSuccess();
};

if (!provider || !version?.content) {
Expand All @@ -64,6 +70,10 @@ export const ModalConfirmationVersion = ({
},
});

if (!version) {
return null;
}

return (
<Modal
isOpen
Expand Down Expand Up @@ -102,7 +112,7 @@ export const ModalConfirmationVersion = ({
</Button>
</>
}
size={ModalSize.SMALL}
size={ModalSize.MEDIUM}
title={
<Text
as="h1"
Expand All @@ -111,17 +121,17 @@ export const ModalConfirmationVersion = ({
$size="h6"
$align="flex-start"
>
{t('Warning')}
{t('Restoring an older version')}
</Text>
}
>
<ModalStyle />
<Box className="--docs--modal-confirmation-version">
<Box>
<Text $variation="secondary" as="p">
{t('Your current document will revert to this version.')}
</Text>
<Text $variation="secondary" as="p">
{t('If a member is editing, his works can be lost.')}
<Text $variation="secondary" as="p" $margin="none">
{t(
"The current document will be replaced, but you'll still find it in the version history.",
)}
</Text>
</Box>
</Box>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,8 @@ export const ModalSelectVersion = ({
$height="calc(100vh - 2em - 12px)"
$css={css`
overflow-y: hidden;
border-left: 1px solid var(--c--globals--colors--gray-200);
border-left: 1px solid
var(--c--contextuals--border--surface--primary);
`}
>
<Box
Expand All @@ -130,7 +131,8 @@ export const ModalSelectVersion = ({
$direction="row"
$align="center"
$css={css`
border-bottom: 1px solid var(--c--globals--colors--gray-200);
border-bottom: 1px solid
var(--c--contextuals--border--surface--primary);
`}
$padding="sm"
>
Expand All @@ -155,7 +157,8 @@ export const ModalSelectVersion = ({
<Box
$padding="xs"
$css={css`
border-top: 1px solid var(--c--globals--colors--gray-200);
border-top: 1px solid
var(--c--contextuals--border--surface--primary);
`}
>
<Button
Expand All @@ -174,6 +177,9 @@ export const ModalSelectVersion = ({
<ModalConfirmationVersion
onClose={() => {
restoreModal.close();
}}
onSuccess={() => {
restoreModal.close();
onClose();
setSelectedVersionId(undefined);
}}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,94 +1,38 @@
import dynamic from 'next/dynamic';
import { useState } from 'react';
import { useTranslation } from 'react-i18next';

import { Box, BoxButton, Text } from '@/components';
import { BoxButton, Text } from '@/components';
import { useCunninghamTheme } from '@/cunningham';
import { Doc } from '@/docs/doc-management';

import { Versions } from '../types';

const ModalConfirmationVersion = dynamic(
() =>
import('./ModalConfirmationVersion').then((mod) => ({
default: mod.ModalConfirmationVersion,
})),
{ ssr: false },
);

interface VersionItemProps {
docId: Doc['id'];
text: string;
versionId?: Versions['version_id'];
isActive: boolean;
onSelect?: () => void;
}

export const VersionItem = ({
docId,
versionId,
text,
isActive,
onSelect,
}: VersionItemProps) => {
export const VersionItem = ({ text, isActive, onSelect }: VersionItemProps) => {
const { t } = useTranslation();
const { colorsTokens, spacingsTokens } = useCunninghamTheme();

const [isModalVersionOpen, setIsModalVersionOpen] = useState(false);
const { spacingsTokens } = useCunninghamTheme();

return (
<>
<BoxButton
aria-label={t('Restore version of {{date}}', { date: text })}
aria-pressed={isActive}
$width="100%"
$css={`
&:focus-visible {
background: var(--c--globals--colors--gray-100);
border-radius: var(--c--globals--spacings--st);
}
`}
className="version-item"
onClick={onSelect}
>
<Box
$width="100%"
as="span"
$background={isActive ? colorsTokens['gray-100'] : 'transparent'}
$radius={spacingsTokens['3xs']}
$css={`
cursor: pointer;

&:hover {
background: ${colorsTokens['gray-100']};
}
`}
$hasTransition
$minWidth="13rem"
className="--docs--version-item"
>
<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">
{text}
</Text>
</Box>
</Box>
</Box>
</BoxButton>
{isModalVersionOpen && versionId && (
<ModalConfirmationVersion
onClose={() => setIsModalVersionOpen(false)}
docId={docId}
versionId={versionId}
/>
)}
</>
<BoxButton
aria-label={t('Restore version of {{date}}', { date: text })}
aria-pressed={isActive}
$width="100%"
$css={`
background: ${isActive ? 'var(--c--contextuals--background--semantic--overlay--primary)' : 'transparent'};
&:focus-visible, &:hover {
background: var(--c--contextuals--background--semantic--overlay--primary);
}
`}
className="version-item --docs--version-item"
onClick={onSelect}
$radius={spacingsTokens['3xs']}
$padding={{ vertical: 'm', horizontal: 'xs' }}
$hasTransition
>
<Text $weight="bold" $size="sm" $textAlign="left">
{text}
</Text>
</BoxButton>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,18 @@ interface VersionListStateProps {
isLoading: boolean;
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 { formatDate } = useDate();
const { formatDateSpecial } = useDate();

if (isLoading) {
return (
Expand All @@ -41,19 +38,17 @@ const VersionListState = ({
}

return (
<Box $gap="10px" $padding="xs">
<Box $gap="xxs" $padding="xs">
{versions?.map((version) => {
const formattedDate = formatDate(
const formattedDate = formatDateSpecial(
version.last_modified,
DateTime.DATETIME_MED,
'dd MMMM · HH:mm',
);
const isSelected = version.version_id === selectedVersionId;
return (
<Box as="li" key={version.version_id} $css="list-style: none;">
<VersionItem
versionId={version.version_id}
text={formattedDate}
docId={doc.id}
isActive={isSelected}
onSelect={() => onSelectVersion?.(version.version_id)}
/>
Expand Down Expand Up @@ -142,7 +137,6 @@ export const VersionList = ({
isLoading={isLoading}
error={error}
versions={versions}
doc={doc}
selectedVersionId={selectedVersionId}
/>
</InfiniteScroll>
Expand Down
6 changes: 5 additions & 1 deletion src/frontend/apps/impress/src/hooks/useDate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ export const useDate = () => {
.toLocaleString(format);
};

const formatDateSpecial = (date: string, format: string): string => {
return DateTime.fromISO(date).setLocale(i18n.language).toFormat(format);
};

const relativeDate = (date: string): string => {
const dateToCompare = DateTime.fromISO(date);

Expand All @@ -45,5 +49,5 @@ export const useDate = () => {
),
);

return { formatDate, relativeDate, calculateDaysLeft };
return { formatDate, formatDateSpecial, relativeDate, calculateDaysLeft };
};
Loading