✨(frontend) remove document
We can now remove a document from a modal.
This commit is contained in:
10
CHANGELOG.md
10
CHANGELOG.md
@@ -8,6 +8,16 @@ and this project adheres to
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## Added
|
||||
|
||||
- Update document (#68)
|
||||
- Remove document (#68)
|
||||
|
||||
## Changed
|
||||
|
||||
- Generate PDF from a modal (#68)
|
||||
- Remove trigger workflow on push tags on CI (#68)
|
||||
|
||||
## [0.1.0] - 2024-05-24
|
||||
|
||||
## Added
|
||||
|
||||
@@ -102,4 +102,37 @@ test.describe('Pad Tools', () => {
|
||||
page.getByRole('checkbox', { name: 'Is it public ?' }),
|
||||
).not.toBeChecked();
|
||||
});
|
||||
|
||||
test('it deletes the pad', async ({ page, browserName }) => {
|
||||
const [randomPad] = await createPad(page, 'pad-delete', browserName, 1);
|
||||
await expect(page.locator('h2').getByText(randomPad)).toBeVisible();
|
||||
|
||||
await page.getByLabel('Open the document options').click();
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: 'Delete document',
|
||||
})
|
||||
.click();
|
||||
|
||||
await expect(
|
||||
page.locator('h2').getByText(`Deleting the document "${randomPad}"`),
|
||||
).toBeVisible();
|
||||
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: 'Confirm deletion',
|
||||
})
|
||||
.click();
|
||||
|
||||
await expect(
|
||||
page.getByText('The document has been deleted.'),
|
||||
).toBeVisible();
|
||||
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Create a new pad' }),
|
||||
).toBeVisible();
|
||||
|
||||
const panel = page.getByLabel('Pads panel').first();
|
||||
await expect(panel.locator('li').getByText(randomPad)).toBeHidden();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { Box, DropButton, IconOptions, Text } from '@/components';
|
||||
import { Pad } from '@/features/pads/pad';
|
||||
import { ModalUpdatePad } from '@/features/pads/pads-create';
|
||||
import { ModalRemovePad, ModalUpdatePad } from '@/features/pads/pads-create';
|
||||
|
||||
import { TemplatesOrdering, useTemplates } from '../api/useTemplates';
|
||||
|
||||
@@ -20,6 +20,7 @@ export const PadToolBox = ({ pad }: PadToolBoxProps) => {
|
||||
ordering: TemplatesOrdering.BY_CREATED_ON_DESC,
|
||||
});
|
||||
const [isModalUpdateOpen, setIsModalUpdateOpen] = useState(false);
|
||||
const [isModalRemoveOpen, setIsModalRemoveOpen] = useState(false);
|
||||
const [isModalPDFOpen, setIsModalPDFOpen] = useState(false);
|
||||
const [isDropOpen, setIsDropOpen] = useState(false);
|
||||
|
||||
@@ -63,6 +64,16 @@ export const PadToolBox = ({ pad }: PadToolBoxProps) => {
|
||||
>
|
||||
<Text $theme="primary">{t('Update document')}</Text>
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setIsModalRemoveOpen(true);
|
||||
setIsDropOpen(false);
|
||||
}}
|
||||
color="primary-text"
|
||||
icon={<span className="material-icons">delete</span>}
|
||||
>
|
||||
<Text $theme="primary">{t('Delete document')}</Text>
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setIsModalPDFOpen(true);
|
||||
@@ -85,6 +96,9 @@ export const PadToolBox = ({ pad }: PadToolBoxProps) => {
|
||||
{isModalUpdateOpen && (
|
||||
<ModalUpdatePad onClose={() => setIsModalUpdateOpen(false)} pad={pad} />
|
||||
)}
|
||||
{isModalRemoveOpen && (
|
||||
<ModalRemovePad onClose={() => setIsModalRemoveOpen(false)} pad={pad} />
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import {
|
||||
UseMutationOptions,
|
||||
useMutation,
|
||||
useQueryClient,
|
||||
} from '@tanstack/react-query';
|
||||
|
||||
import { APIError, errorCauses, fetchAPI } from '@/api';
|
||||
|
||||
import { KEY_LIST_PAD } from '../../pads-panel';
|
||||
|
||||
interface RemovePadProps {
|
||||
padId: string;
|
||||
}
|
||||
|
||||
export const removePad = async ({ padId }: RemovePadProps): Promise<void> => {
|
||||
const response = await fetchAPI(`documents/${padId}/`, {
|
||||
method: 'DELETE',
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new APIError('Failed to delete the pad', await errorCauses(response));
|
||||
}
|
||||
};
|
||||
|
||||
type UseRemovePadOptions = UseMutationOptions<void, APIError, RemovePadProps>;
|
||||
|
||||
export const useRemovePad = (options?: UseRemovePadOptions) => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation<void, APIError, RemovePadProps>({
|
||||
mutationFn: removePad,
|
||||
...options,
|
||||
onSuccess: (data, variables, context) => {
|
||||
void queryClient.invalidateQueries({
|
||||
queryKey: [KEY_LIST_PAD],
|
||||
});
|
||||
if (options?.onSuccess) {
|
||||
options.onSuccess(data, variables, context);
|
||||
}
|
||||
},
|
||||
onError: (error, variables, context) => {
|
||||
if (options?.onError) {
|
||||
options.onError(error, variables, context);
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
<svg
|
||||
class="svg-icon"
|
||||
style="vertical-align: middle; overflow: hidden"
|
||||
viewBox="0 0 1024 1024"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M883.2 889.6c0 57.6-44.8 102.4-102.4 102.4h-550.4c-57.6 0-102.4-44.8-102.4-102.4v-665.6c0-57.6 44.8-102.4 102.4-102.4h550.4c57.6 0 102.4 44.8 102.4 102.4v665.6z m-57.6-665.6c0-25.6-19.2-44.8-44.8-44.8h-550.4c-25.6 0-44.8 19.2-44.8 44.8v665.6c0 25.6 19.2 44.8 44.8 44.8h550.4c25.6 0 44.8-19.2 44.8-44.8v-665.6z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M723.2 883.2h-236.8c-6.4 0-12.8-6.4-12.8-12.8s6.4-12.8 12.8-12.8h236.8c25.6 0 38.4-25.6 38.4-70.4v-44.8c0-6.4 6.4-12.8 12.8-12.8s12.8 6.4 12.8 12.8v44.8c0 57.6-25.6 96-64 96zM774.4 684.8c-6.4 0-6.4 0-12.8-6.4 0 0-6.4-6.4-6.4-12.8s0-6.4 6.4-12.8 12.8-6.4 19.2 0c0 0 6.4 6.4 6.4 12.8s0 6.4-6.4 12.8c0 6.4 0 6.4-6.4 6.4zM281.6 281.6c-12.8 0-32-12.8-32-25.6v-185.6c0-12.8 12.8-25.6 32-25.6 12.8 0 25.6 12.8 25.6 25.6v185.6c6.4 12.8-12.8 25.6-25.6 25.6zM441.6 281.6c-12.8 0-32-12.8-32-25.6v-185.6c0-12.8 12.8-25.6 32-25.6 12.8 0 25.6 12.8 25.6 25.6v185.6c0 12.8-12.8 25.6-25.6 25.6zM595.2 281.6c-12.8 0-32-12.8-32-25.6v-185.6c0-12.8 12.8-25.6 32-25.6 12.8 0 25.6 12.8 25.6 25.6v185.6c6.4 12.8-6.4 25.6-25.6 25.6zM755.2 281.6c-12.8 0-32-12.8-32-25.6v-185.6c0-12.8 12.8-25.6 32-25.6 12.8 0 25.6 12.8 25.6 25.6v185.6c6.4 12.8-6.4 25.6-25.6 25.6z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M857.6 396.8h-691.2c-6.4 0-12.8-6.4-12.8-12.8s6.4-12.8 12.8-12.8h691.2c6.4 0 12.8 6.4 12.8 12.8s0 12.8-12.8 12.8z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,6 @@
|
||||
<svg viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M12 38C12 40.2 13.8 42 16 42H32C34.2 42 36 40.2 36 38V14H12V38ZM18.34 25.18C17.56 24.4 17.56 23.14 18.34 22.36C19.12 21.58 20.38 21.58 21.16 22.36L24 25.18L26.82 22.36C27.6 21.58 28.86 21.58 29.64 22.36C30.42 23.14 30.42 24.4 29.64 25.18L26.82 28L29.64 30.82C30.42 31.6 30.42 32.86 29.64 33.64C28.86 34.42 27.6 34.42 26.82 33.64L24 30.82L21.18 33.64C20.4 34.42 19.14 34.42 18.36 33.64C17.58 32.86 17.58 31.6 18.36 30.82L21.18 28L18.34 25.18ZM36 8H31L29.58 6.58C29.22 6.22 28.7 6 28.18 6H19.82C19.3 6 18.78 6.22 18.42 6.58L17 8H12C10.9 8 10 8.9 10 10C10 11.1 10.9 12 12 12H36C37.1 12 38 11.1 38 10C38 8.9 37.1 8 36 8Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 742 B |
@@ -0,0 +1,124 @@
|
||||
import {
|
||||
Button,
|
||||
Modal,
|
||||
ModalSize,
|
||||
VariantType,
|
||||
useToastProvider,
|
||||
} from '@openfun/cunningham-react';
|
||||
import { t } from 'i18next';
|
||||
import { useRouter } from 'next/navigation';
|
||||
|
||||
import { Box, Text, TextErrors } from '@/components';
|
||||
import useCunninghamTheme from '@/cunningham/useCunninghamTheme';
|
||||
|
||||
import { Pad } from '../../pad/types';
|
||||
import { useRemovePad } from '../api/useRemovePad';
|
||||
import IconPad from '../assets/icon-pad.svg';
|
||||
import IconRemove from '../assets/icon-trash.svg';
|
||||
|
||||
interface ModalRemovePadProps {
|
||||
onClose: () => void;
|
||||
pad: Pad;
|
||||
}
|
||||
|
||||
export const ModalRemovePad = ({ onClose, pad }: ModalRemovePadProps) => {
|
||||
const { colorsTokens } = useCunninghamTheme();
|
||||
const { toast } = useToastProvider();
|
||||
const router = useRouter();
|
||||
|
||||
const {
|
||||
mutate: removePad,
|
||||
isError,
|
||||
error,
|
||||
} = useRemovePad({
|
||||
onSuccess: () => {
|
||||
toast(t('The document has been deleted.'), VariantType.SUCCESS, {
|
||||
duration: 4000,
|
||||
});
|
||||
router.push('/');
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<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('Confirm deletion')}
|
||||
color="primary"
|
||||
fullWidth
|
||||
onClick={() =>
|
||||
removePad({
|
||||
padId: pad.id,
|
||||
})
|
||||
}
|
||||
>
|
||||
{t('Confirm deletion')}
|
||||
</Button>
|
||||
}
|
||||
size={ModalSize.MEDIUM}
|
||||
title={
|
||||
<Box $align="center" $gap="1rem">
|
||||
<IconRemove width={48} color={colorsTokens()['primary-text']} />
|
||||
<Text as="h2" $size="h3" $margin="none">
|
||||
{t('Deleting the document "{{title}}"', { title: pad.title })}
|
||||
</Text>
|
||||
</Box>
|
||||
}
|
||||
>
|
||||
<Box
|
||||
$margin={{ bottom: 'xl' }}
|
||||
aria-label={t('Content modal to delete document')}
|
||||
>
|
||||
<Text as="p" $margin={{ bottom: 'big' }}>
|
||||
{t('Are you sure you want to delete the document "{{title}}"?', {
|
||||
title: pad.title,
|
||||
})}
|
||||
</Text>
|
||||
|
||||
{isError && (
|
||||
<TextErrors $margin={{ bottom: 'small' }} causes={error.cause} />
|
||||
)}
|
||||
|
||||
<Text
|
||||
as="p"
|
||||
$padding="small"
|
||||
$direction="row"
|
||||
$gap="0.5rem"
|
||||
$background={colorsTokens()['primary-150']}
|
||||
$theme="primary"
|
||||
$align="center"
|
||||
$radius="2px"
|
||||
>
|
||||
<IconPad
|
||||
className="p-t"
|
||||
aria-label={t(`Document icon`)}
|
||||
color={colorsTokens()['primary-500']}
|
||||
width={58}
|
||||
style={{
|
||||
borderRadius: '8px',
|
||||
backgroundColor: '#ffffff',
|
||||
border: `1px solid ${colorsTokens()['primary-300']}`,
|
||||
}}
|
||||
/>
|
||||
<Text $theme="primary" $weight="bold" $size="l">
|
||||
{pad.title}
|
||||
</Text>
|
||||
</Text>
|
||||
</Box>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
@@ -1,2 +1,3 @@
|
||||
export * from './CardCreatePad';
|
||||
export * from './ModalRemovePad';
|
||||
export * from './ModalUpdatePad';
|
||||
|
||||
Reference in New Issue
Block a user