🔥(frontend) remove docs-panel feature
Remove docs-panel feature, we will replace it with a datagrid.
This commit is contained in:
@@ -1,159 +0,0 @@
|
||||
import { expect, test } from '@playwright/test';
|
||||
|
||||
import { waitForElementCount } from '../helpers';
|
||||
|
||||
import { createDoc } from './common';
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/');
|
||||
});
|
||||
|
||||
test.describe('Documents Panel', () => {
|
||||
test('checks all the elements are visible', async ({ page }) => {
|
||||
const panel = page.getByLabel('Documents panel').first();
|
||||
|
||||
await expect(panel.getByText('Documents')).toBeVisible();
|
||||
|
||||
await expect(
|
||||
panel.getByRole('button', {
|
||||
name: 'Sort the documents',
|
||||
}),
|
||||
).toBeVisible();
|
||||
|
||||
await expect(
|
||||
panel.getByRole('button', {
|
||||
name: 'Add a document',
|
||||
}),
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test('checks the sort button', async ({ page }) => {
|
||||
const responsePromiseSortDesc = page.waitForResponse(
|
||||
(response) =>
|
||||
response.url().includes('/documents/?page=1&ordering=-created_at') &&
|
||||
response.status() === 200,
|
||||
);
|
||||
|
||||
const responsePromiseSortAsc = page.waitForResponse(
|
||||
(response) =>
|
||||
response.url().includes('/documents/?page=1&ordering=created_at') &&
|
||||
response.status() === 200,
|
||||
);
|
||||
|
||||
const panel = page.getByLabel('Documents panel').first();
|
||||
|
||||
await panel
|
||||
.getByRole('button', {
|
||||
name: 'Sort the documents by creation date ascendent',
|
||||
})
|
||||
.click();
|
||||
|
||||
const responseSortAsc = await responsePromiseSortAsc;
|
||||
expect(responseSortAsc.ok()).toBeTruthy();
|
||||
|
||||
await panel
|
||||
.getByRole('button', {
|
||||
name: 'Sort the documents by creation date descendent',
|
||||
})
|
||||
.click();
|
||||
|
||||
const responseSortDesc = await responsePromiseSortDesc;
|
||||
expect(responseSortDesc.ok()).toBeTruthy();
|
||||
});
|
||||
|
||||
test('checks the infinite scroll', async ({ page }) => {
|
||||
await page.route(
|
||||
/.*\/documents\/\?page=.*&ordering=-created_at/,
|
||||
async (route) => {
|
||||
const request = route.request();
|
||||
const url = new URL(request.url());
|
||||
const pageId = url.searchParams.get('page');
|
||||
const documents = {
|
||||
count: 40,
|
||||
next: 'http://localhost:3000/documents/?page=2&ordering=-created_at',
|
||||
previous: null,
|
||||
results: Array.from({ length: 20 }, (_, i) => ({
|
||||
id: `2ff-${pageId}-${i}`,
|
||||
title: `My document-${pageId}-${i}`,
|
||||
accesses: [
|
||||
{
|
||||
id: 'b644e9b1-0517-4cfb-90ca-f7d6f2f6bb9a',
|
||||
role: `owner`,
|
||||
team: '',
|
||||
user: {
|
||||
id: 'a4743608-c9d8-4692-bef4-f795e25a3a88',
|
||||
email: 'user@chromium.e2e',
|
||||
},
|
||||
},
|
||||
],
|
||||
content: '',
|
||||
is_public: true,
|
||||
abilities: {},
|
||||
})),
|
||||
};
|
||||
|
||||
if (request.method().includes('GET')) {
|
||||
await route.fulfill({
|
||||
json: documents,
|
||||
});
|
||||
} else {
|
||||
await route.continue();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
await page.route(`**/documents/2ff-1-16/`, async (route) => {
|
||||
const request = route.request();
|
||||
|
||||
if (request.method().includes('GET')) {
|
||||
await route.fulfill({
|
||||
json: {
|
||||
id: '2ff-1-16',
|
||||
title: 'My document-1-16',
|
||||
content: '',
|
||||
abilities: {
|
||||
partial_update: true,
|
||||
},
|
||||
accesses: [
|
||||
{
|
||||
id: 'b644e9b1-0517-4cfb-90ca-f7d6f2f6bb9a',
|
||||
role: `owner`,
|
||||
team: '',
|
||||
user: {
|
||||
id: 'a4743608-c9d8-4692-bef4-f795e25a3a88',
|
||||
email: '',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
} else {
|
||||
await route.continue();
|
||||
}
|
||||
});
|
||||
|
||||
const panel = page.getByLabel('Documents panel').first();
|
||||
await expect(panel.locator('li')).toHaveCount(20);
|
||||
await panel.getByText(`My document-1-16`).click();
|
||||
|
||||
await waitForElementCount(panel.locator('li'), 21, 10000);
|
||||
expect(await panel.locator('li').count()).toBeGreaterThan(20);
|
||||
await expect(panel.getByText(`My document-1-16`)).toBeVisible();
|
||||
await expect(panel.getByText(`My document-2-15`)).toBeVisible();
|
||||
});
|
||||
|
||||
test('checks the hover and selected state', async ({ page, browserName }) => {
|
||||
const panel = page.getByLabel('Documents panel').first();
|
||||
await createDoc(page, 'doc-hover', browserName, 2);
|
||||
|
||||
const selectedDoc = panel.locator('li').nth(0);
|
||||
await expect(selectedDoc).toHaveCSS(
|
||||
'background-color',
|
||||
'rgb(202, 202, 251)',
|
||||
);
|
||||
|
||||
const hoverDoc = panel.locator('li').nth(1);
|
||||
await hoverDoc.hover();
|
||||
await expect(hoverDoc).toHaveCSS('background-color', 'rgb(227, 227, 253)');
|
||||
});
|
||||
});
|
||||
@@ -1,168 +0,0 @@
|
||||
import '@testing-library/jest-dom';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import fetchMock from 'fetch-mock';
|
||||
|
||||
import { AppWrapper } from '@/tests/utils';
|
||||
|
||||
import { DocList } from '../components/DocList';
|
||||
import { Panel } from '../components/Panel';
|
||||
|
||||
window.HTMLElement.prototype.scroll = function () {};
|
||||
|
||||
jest.mock('next/router', () => ({
|
||||
...jest.requireActual('next/router'),
|
||||
useRouter: () => ({
|
||||
query: {},
|
||||
}),
|
||||
}));
|
||||
|
||||
describe('PanelDocs', () => {
|
||||
afterEach(() => {
|
||||
fetchMock.restore();
|
||||
});
|
||||
|
||||
it('renders with no doc to display', async () => {
|
||||
fetchMock.mock(`end:/documents/?page=1&ordering=-created_at`, {
|
||||
count: 0,
|
||||
results: [],
|
||||
});
|
||||
|
||||
render(<DocList />, { wrapper: AppWrapper });
|
||||
|
||||
expect(screen.getByRole('status')).toBeInTheDocument();
|
||||
|
||||
expect(
|
||||
await screen.findByText(
|
||||
'Create your first document by clicking on the "Create a new document" button.',
|
||||
),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders an empty doc', async () => {
|
||||
fetchMock.mock(`end:/documents/?page=1&ordering=-created_at`, {
|
||||
count: 1,
|
||||
results: [
|
||||
{
|
||||
id: '1',
|
||||
name: 'Team 1',
|
||||
accesses: [],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
render(<DocList />, { wrapper: AppWrapper });
|
||||
|
||||
expect(screen.getByRole('status')).toBeInTheDocument();
|
||||
|
||||
expect(await screen.findByLabelText('Empty docs icon')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders a doc with only 1 member', async () => {
|
||||
fetchMock.mock(`end:/documents/?page=1&ordering=-created_at`, {
|
||||
count: 1,
|
||||
results: [
|
||||
{
|
||||
id: '1',
|
||||
name: 'Team 1',
|
||||
accesses: [
|
||||
{
|
||||
id: '1',
|
||||
role: 'owner',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
render(<DocList />, { wrapper: AppWrapper });
|
||||
|
||||
expect(screen.getByRole('status')).toBeInTheDocument();
|
||||
|
||||
expect(await screen.findByLabelText('Empty docs icon')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders a non-empty doc', async () => {
|
||||
fetchMock.mock(`end:/documents/?page=1&ordering=-created_at`, {
|
||||
count: 1,
|
||||
results: [
|
||||
{
|
||||
id: '1',
|
||||
name: 'Doc 1',
|
||||
accesses: [
|
||||
{
|
||||
id: '1',
|
||||
role: 'admin',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
role: 'member',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
render(<DocList />, { wrapper: AppWrapper });
|
||||
|
||||
expect(screen.getByRole('status')).toBeInTheDocument();
|
||||
|
||||
expect(await screen.findByLabelText('Docs icon')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders the error', async () => {
|
||||
fetchMock.mock(`end:/documents/?page=1&ordering=-created_at`, {
|
||||
status: 500,
|
||||
});
|
||||
|
||||
render(<DocList />, { wrapper: AppWrapper });
|
||||
|
||||
expect(screen.getByRole('status')).toBeInTheDocument();
|
||||
|
||||
expect(
|
||||
await screen.findByText('Something bad happens, please retry.'),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders with doc panel open', async () => {
|
||||
fetchMock.mock(`end:/documents/?page=1&ordering=-created_at`, {
|
||||
count: 1,
|
||||
results: [],
|
||||
});
|
||||
|
||||
render(<Panel />, { wrapper: AppWrapper });
|
||||
|
||||
expect(
|
||||
screen.getByRole('button', { name: 'Close the documents panel' }),
|
||||
).toBeVisible();
|
||||
|
||||
expect(await screen.findByText('Documents')).toBeVisible();
|
||||
});
|
||||
|
||||
it('closes and opens the doc panel', async () => {
|
||||
fetchMock.mock(`end:/documents/?page=1&ordering=-created_at`, {
|
||||
count: 1,
|
||||
results: [],
|
||||
});
|
||||
|
||||
render(<Panel />, { wrapper: AppWrapper });
|
||||
|
||||
expect(await screen.findByText('Documents')).toBeVisible();
|
||||
|
||||
await userEvent.click(
|
||||
screen.getByRole('button', {
|
||||
name: 'Close the documents panel',
|
||||
}),
|
||||
);
|
||||
|
||||
expect(await screen.findByText('Documents')).not.toBeVisible();
|
||||
|
||||
await userEvent.click(
|
||||
screen.getByRole('button', {
|
||||
name: 'Open the documents panel',
|
||||
}),
|
||||
);
|
||||
|
||||
expect(await screen.findByText('Documents')).toBeVisible();
|
||||
});
|
||||
});
|
||||
@@ -1,6 +0,0 @@
|
||||
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M20.75 0.75H3.25C1.8625 0.75 0.75 1.875 0.75 3.25V20.75C0.75 22.125 1.8625 23.25 3.25 23.25H20.75C22.125 23.25 23.25 22.125 23.25 20.75V3.25C23.25 1.875 22.125 0.75 20.75 0.75ZM17 13.25H13.25V17C13.25 17.6875 12.6875 18.25 12 18.25C11.3125 18.25 10.75 17.6875 10.75 17V13.25H7C6.3125 13.25 5.75 12.6875 5.75 12C5.75 11.3125 6.3125 10.75 7 10.75H10.75V7C10.75 6.3125 11.3125 5.75 12 5.75C12.6875 5.75 13.25 6.3125 13.25 7V10.75H17C17.6875 10.75 18.25 11.3125 18.25 12C18.25 12.6875 17.6875 13.25 17 13.25Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 630 B |
@@ -1,13 +0,0 @@
|
||||
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_508_5524)">
|
||||
<path
|
||||
d="M12 2C6.48 2 2 6.48 2 12C2 17.52 6.48 22 12 22C17.52 22 22 17.52 22 12C22 6.48 17.52 2 12 2ZM4 12C4 7.58 7.58 4 12 4C13.85 4 15.55 4.63 16.9 5.69L5.69 16.9C4.63 15.55 4 13.85 4 12ZM12 20C10.15 20 8.45 19.37 7.1 18.31L18.31 7.1C19.37 8.45 20 10.15 20 12C20 16.42 16.42 20 12 20Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_508_5524">
|
||||
<rect width="24" height="24" fill="white" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 578 B |
@@ -1,13 +0,0 @@
|
||||
<svg viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_178_17837)">
|
||||
<path
|
||||
d="M11.25 3.75L6.25 8.7375H10V17.5H12.5V8.7375H16.25L11.25 3.75ZM20 21.2625V12.5H17.5V21.2625H13.75L18.75 26.25L23.75 21.2625H20Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_178_17837">
|
||||
<rect width="30" height="30" fill="white" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 429 B |
@@ -1,102 +0,0 @@
|
||||
import { useRouter } from 'next/router';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import IconGroup from '@/assets/icons/icon-group.svg';
|
||||
import { Box, StyledLink, Text } from '@/components';
|
||||
import { useCunninghamTheme } from '@/cunningham';
|
||||
import { Doc } from '@/features/docs/doc-management';
|
||||
|
||||
import IconNone from '../assets/icon-none.svg';
|
||||
|
||||
interface DocItemProps {
|
||||
doc: Doc;
|
||||
}
|
||||
|
||||
export const DocItem = ({ doc }: DocItemProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { colorsTokens } = useCunninghamTheme();
|
||||
const {
|
||||
query: { id },
|
||||
} = useRouter();
|
||||
|
||||
// There is at least 1 owner in the team
|
||||
const hasMembers = doc.accesses.length > 1;
|
||||
const isActive = doc.id === id;
|
||||
|
||||
const commonProps = {
|
||||
className: 'p-t',
|
||||
width: 52,
|
||||
style: {
|
||||
borderRadius: '10px',
|
||||
flexShrink: 0,
|
||||
background: '#fff',
|
||||
},
|
||||
};
|
||||
|
||||
const activeStyle = `
|
||||
border-right: 4px solid ${colorsTokens()['primary-600']};
|
||||
background: ${colorsTokens()['primary-400']};
|
||||
span{
|
||||
color: ${colorsTokens()['primary-text']};
|
||||
}
|
||||
`;
|
||||
|
||||
const hoverStyle = `
|
||||
&:hover{
|
||||
border-right: 4px solid ${colorsTokens()['primary-400']};
|
||||
background: ${colorsTokens()['primary-300']};
|
||||
|
||||
span{
|
||||
color: ${colorsTokens()['primary-text']};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
return (
|
||||
<Box
|
||||
$margin="none"
|
||||
as="li"
|
||||
$css={`
|
||||
transition: all 0.2s ease-in;
|
||||
border-right: 4px solid transparent;
|
||||
${isActive ? activeStyle : hoverStyle}
|
||||
`}
|
||||
>
|
||||
<StyledLink className="p-s pt-t pb-t" href={`/docs/${doc.id}`}>
|
||||
<Box $align="center" $direction="row" $gap="0.5rem">
|
||||
{hasMembers ? (
|
||||
<IconGroup
|
||||
aria-label={t(`Docs icon`)}
|
||||
color={colorsTokens()['primary-500']}
|
||||
{...commonProps}
|
||||
style={{
|
||||
...commonProps.style,
|
||||
border: `1px solid ${colorsTokens()['primary-300']}`,
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<IconNone
|
||||
aria-label={t(`Empty docs icon`)}
|
||||
color={colorsTokens()['greyscale-500']}
|
||||
{...commonProps}
|
||||
style={{
|
||||
...commonProps.style,
|
||||
border: `1px solid ${colorsTokens()['greyscale-300']}`,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<Text
|
||||
$weight="bold"
|
||||
$color={!hasMembers ? colorsTokens()['greyscale-600'] : undefined}
|
||||
$css={`
|
||||
min-width: 14rem;
|
||||
`}
|
||||
>
|
||||
{doc.title}
|
||||
</Text>
|
||||
</Box>
|
||||
</StyledLink>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
@@ -1,112 +0,0 @@
|
||||
import { Loader } from '@openfun/cunningham-react';
|
||||
import React, { useMemo, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { APIError } from '@/api';
|
||||
import { Box, Text, TextErrors } from '@/components';
|
||||
import { InfiniteScroll } from '@/components/InfiniteScroll';
|
||||
import { Doc, useDocs } from '@/features/docs/doc-management';
|
||||
|
||||
import { useDocPanelStore } from '../store';
|
||||
|
||||
import { DocItem } from './DocItem';
|
||||
|
||||
interface PanelTeamsStateProps {
|
||||
isLoading: boolean;
|
||||
error: APIError<unknown> | null;
|
||||
docs?: Doc[];
|
||||
}
|
||||
|
||||
const DocListState = ({ isLoading, error, docs }: PanelTeamsStateProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<Box $align="center" $margin="large">
|
||||
<Loader />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
if (!docs?.length && !error) {
|
||||
return (
|
||||
<Box $justify="center" $margin="small">
|
||||
<Text
|
||||
as="p"
|
||||
$margin={{ vertical: 'none' }}
|
||||
$theme="greyscale"
|
||||
$variation="500"
|
||||
>
|
||||
{t('0 group to display.')}
|
||||
</Text>
|
||||
<Text as="p" $theme="greyscale" $variation="500">
|
||||
{t(
|
||||
'Create your first document by clicking on the "Create a new document" button.',
|
||||
)}
|
||||
</Text>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{docs?.map((doc) => <DocItem doc={doc} key={doc.id} />)}
|
||||
{error && (
|
||||
<Box
|
||||
$justify="center"
|
||||
$margin={{ vertical: 'big', horizontal: 'auto' }}
|
||||
>
|
||||
<TextErrors
|
||||
causes={error.cause}
|
||||
icon={
|
||||
error.status === 502 ? (
|
||||
<Text className="material-icons" $theme="danger">
|
||||
wifi_off
|
||||
</Text>
|
||||
) : undefined
|
||||
}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const DocList = () => {
|
||||
const ordering = useDocPanelStore((state) => state.ordering);
|
||||
const {
|
||||
data,
|
||||
error,
|
||||
isLoading,
|
||||
fetchNextPage,
|
||||
hasNextPage,
|
||||
isFetchingNextPage,
|
||||
} = useDocs({
|
||||
ordering,
|
||||
});
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const docs = useMemo(() => {
|
||||
return data?.pages.reduce((acc, page) => {
|
||||
return acc.concat(page.results);
|
||||
}, [] as Doc[]);
|
||||
}, [data?.pages]);
|
||||
|
||||
return (
|
||||
<Box $css="overflow-y: auto; overflow-x: hidden;" ref={containerRef}>
|
||||
<InfiniteScroll
|
||||
hasMore={hasNextPage}
|
||||
isLoading={isFetchingNextPage}
|
||||
next={() => {
|
||||
void fetchNextPage();
|
||||
}}
|
||||
scrollContainer={containerRef.current}
|
||||
as="ul"
|
||||
$padding="none"
|
||||
$margin={{ top: 'none' }}
|
||||
role="listbox"
|
||||
>
|
||||
<DocListState isLoading={isLoading} error={error} docs={docs} />
|
||||
</InfiniteScroll>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
@@ -1,87 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { Box, BoxButton, Text } from '@/components';
|
||||
import { useCunninghamTheme } from '@/cunningham';
|
||||
|
||||
import { DocList } from './DocList';
|
||||
import { PanelActions } from './PanelActions';
|
||||
|
||||
export const Panel = () => {
|
||||
const { t } = useTranslation();
|
||||
const { colorsTokens } = useCunninghamTheme();
|
||||
|
||||
const [isOpen, setIsOpen] = useState(true);
|
||||
|
||||
const closedOverridingStyles = !isOpen && {
|
||||
$width: '0',
|
||||
$maxWidth: '0',
|
||||
$minWidth: '0',
|
||||
};
|
||||
|
||||
const transition = 'all 0.5s ease-in-out';
|
||||
|
||||
return (
|
||||
<Box
|
||||
$width="100%"
|
||||
$maxWidth="20rem"
|
||||
$minWidth="14rem"
|
||||
$css={`
|
||||
position: relative;
|
||||
border-right: 1px solid ${colorsTokens()['primary-300']};
|
||||
transition: ${transition};
|
||||
`}
|
||||
$height="inherit"
|
||||
aria-label="Documents panel"
|
||||
{...closedOverridingStyles}
|
||||
>
|
||||
<BoxButton
|
||||
className="material-icons"
|
||||
aria-label={
|
||||
isOpen
|
||||
? t('Close the documents panel')
|
||||
: t('Open the documents panel')
|
||||
}
|
||||
$background="white"
|
||||
$color={colorsTokens()['primary-600']}
|
||||
$radius="100%"
|
||||
$padding="0.3rem"
|
||||
$position="absolute"
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
$css={`
|
||||
right: ${isOpen ? '-1.3' : '-2.8'}rem;
|
||||
top: ${isOpen ? '0.7' : '0.25'}rem;
|
||||
transform: rotate(${isOpen ? '0' : '180'}deg);
|
||||
transition: ${transition};
|
||||
font-size: 1.8rem;
|
||||
border: 1px solid #fafafa;
|
||||
box-shadow: ${isOpen ? '1px 1px' : '-1px -1px'} 3px #dfdfdf;
|
||||
z-index: 1;
|
||||
`}
|
||||
>
|
||||
menu_open
|
||||
</BoxButton>
|
||||
<Box
|
||||
$css={`
|
||||
overflow: hidden;
|
||||
opacity: ${isOpen ? '1' : '0'};
|
||||
transition: ${transition};
|
||||
`}
|
||||
>
|
||||
<Box
|
||||
$padding={{ all: 'small', right: 'large' }}
|
||||
$direction="row"
|
||||
$align="center"
|
||||
$justify="space-between"
|
||||
$css={`border-bottom: 1px solid ${colorsTokens()['primary-300']};`}
|
||||
>
|
||||
<Text $weight="bold" $size="1.25rem">
|
||||
{t('Documents')}
|
||||
</Text>
|
||||
<PanelActions />
|
||||
</Box>
|
||||
<DocList />
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
@@ -1,60 +0,0 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { Box, BoxButton, StyledLink } from '@/components';
|
||||
import { useCunninghamTheme } from '@/cunningham';
|
||||
import { DocsOrdering } from '@/features/docs/doc-management';
|
||||
|
||||
import IconAdd from '../assets/icon-add.svg';
|
||||
import IconSort from '../assets/icon-sort.svg';
|
||||
import { useDocPanelStore } from '../store';
|
||||
|
||||
export const PanelActions = () => {
|
||||
const { t } = useTranslation();
|
||||
const { changeOrdering, ordering } = useDocPanelStore();
|
||||
const { colorsTokens } = useCunninghamTheme();
|
||||
|
||||
const isSortAsc = ordering === DocsOrdering.BY_CREATED_ON;
|
||||
|
||||
return (
|
||||
<Box
|
||||
$direction="row"
|
||||
$gap="1rem"
|
||||
$css={`
|
||||
& button {
|
||||
padding: 0;
|
||||
|
||||
svg {
|
||||
padding: 0.1rem;
|
||||
}
|
||||
}
|
||||
`}
|
||||
>
|
||||
<BoxButton
|
||||
aria-label={
|
||||
isSortAsc
|
||||
? t('Sort the documents by creation date descendent')
|
||||
: t('Sort the documents by creation date ascendent')
|
||||
}
|
||||
onClick={changeOrdering}
|
||||
$radius="100%"
|
||||
$background={isSortAsc ? colorsTokens()['primary-200'] : 'transparent'}
|
||||
$color={colorsTokens()['primary-600']}
|
||||
>
|
||||
<IconSort
|
||||
width={30}
|
||||
height={30}
|
||||
aria-label={t('Sort documents icon')}
|
||||
/>
|
||||
</BoxButton>
|
||||
<StyledLink href="/docs/create">
|
||||
<BoxButton
|
||||
aria-label={t('Add a document')}
|
||||
$color={colorsTokens()['primary-600']}
|
||||
>
|
||||
<IconAdd width={30} height={30} aria-label={t('Add document icon')} />
|
||||
</BoxButton>
|
||||
</StyledLink>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
@@ -1 +0,0 @@
|
||||
export * from './Panel';
|
||||
@@ -1 +0,0 @@
|
||||
export * from './components';
|
||||
@@ -1 +0,0 @@
|
||||
export * from './useDocPanelStore';
|
||||
@@ -1,19 +0,0 @@
|
||||
import { create } from 'zustand';
|
||||
|
||||
import { DocsOrdering } from '@/features/docs/doc-management/api';
|
||||
|
||||
interface DocPanelStore {
|
||||
ordering: DocsOrdering;
|
||||
changeOrdering: () => void;
|
||||
}
|
||||
|
||||
export const useDocPanelStore = create<DocPanelStore>((set) => ({
|
||||
ordering: DocsOrdering.BY_CREATED_ON_DESC,
|
||||
changeOrdering: () =>
|
||||
set(({ ordering }) => ({
|
||||
ordering:
|
||||
ordering === DocsOrdering.BY_CREATED_ON
|
||||
? DocsOrdering.BY_CREATED_ON_DESC
|
||||
: DocsOrdering.BY_CREATED_ON,
|
||||
})),
|
||||
}));
|
||||
@@ -1,3 +1,2 @@
|
||||
export * from './doc-editor';
|
||||
export * from './doc-management';
|
||||
export * from './docs-panel';
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
import { PropsWithChildren } from 'react';
|
||||
|
||||
import { Box } from '@/components';
|
||||
import { useCunninghamTheme } from '@/cunningham';
|
||||
import { Panel } from '@/features/docs/docs-panel';
|
||||
|
||||
import { MainLayout } from './MainLayout';
|
||||
|
||||
export function DocLayout({ children }: PropsWithChildren) {
|
||||
const { colorsTokens } = useCunninghamTheme();
|
||||
|
||||
return (
|
||||
<MainLayout>
|
||||
<Box $height="inherit" $direction="row">
|
||||
<Panel />
|
||||
<Box
|
||||
$background={colorsTokens()['primary-bg']}
|
||||
$width="100%"
|
||||
$height="inherit"
|
||||
>
|
||||
{children}
|
||||
</Box>
|
||||
</Box>
|
||||
</MainLayout>
|
||||
);
|
||||
}
|
||||
@@ -1,3 +1,2 @@
|
||||
export * from './MainLayout';
|
||||
export * from './DocLayout';
|
||||
export * from './PageLayout';
|
||||
|
||||
@@ -6,7 +6,7 @@ import { ReactElement } from 'react';
|
||||
import { Box, Text, TextErrors } from '@/components/';
|
||||
import { DocEditor } from '@/features/docs/doc-editor';
|
||||
import { useDoc } from '@/features/docs/doc-management';
|
||||
import { DocLayout } from '@/layouts';
|
||||
import { MainLayout } from '@/layouts';
|
||||
import { NextPageWithLayout } from '@/types/next';
|
||||
|
||||
const Page: NextPageWithLayout = () => {
|
||||
@@ -63,7 +63,7 @@ const Doc = ({ id }: DocProps) => {
|
||||
};
|
||||
|
||||
Page.getLayout = function getLayout(page: ReactElement) {
|
||||
return <DocLayout>{page}</DocLayout>;
|
||||
return <MainLayout>{page}</MainLayout>;
|
||||
};
|
||||
|
||||
export default Page;
|
||||
|
||||
@@ -2,7 +2,7 @@ import { ReactElement } from 'react';
|
||||
|
||||
import { Box } from '@/components';
|
||||
import { CardCreateDoc } from '@/features/docs/doc-management';
|
||||
import { DocLayout } from '@/layouts';
|
||||
import { MainLayout } from '@/layouts';
|
||||
import { NextPageWithLayout } from '@/types/next';
|
||||
|
||||
const Page: NextPageWithLayout = () => {
|
||||
@@ -14,7 +14,7 @@ const Page: NextPageWithLayout = () => {
|
||||
};
|
||||
|
||||
Page.getLayout = function getLayout(page: ReactElement) {
|
||||
return <DocLayout>{page}</DocLayout>;
|
||||
return <MainLayout>{page}</MainLayout>;
|
||||
};
|
||||
|
||||
export default Page;
|
||||
|
||||
@@ -1,16 +1,3 @@
|
||||
import type { ReactElement } from 'react';
|
||||
|
||||
import { DocLayout } from '@/layouts';
|
||||
import { NextPageWithLayout } from '@/types/next';
|
||||
|
||||
import Docs from './docs';
|
||||
|
||||
const Page: NextPageWithLayout = () => {
|
||||
return <Docs />;
|
||||
};
|
||||
|
||||
Page.getLayout = function getLayout(page: ReactElement) {
|
||||
return <DocLayout>{page}</DocLayout>;
|
||||
};
|
||||
|
||||
export default Page;
|
||||
export default Docs;
|
||||
|
||||
Reference in New Issue
Block a user