(frontend) increase doc visibility options

We now have 3 visibility options for docs:
- public
- restricted
- authenticated

We also have 2 editability options:
- readonly
- editable

The editability options are only available
for public and authenticated docs.
This commit is contained in:
Anthony LC
2024-10-23 11:11:29 +02:00
committed by Anthony LC
parent c0cb12f002
commit ff364f8b3d
13 changed files with 619 additions and 127 deletions

View File

@@ -4,17 +4,17 @@ export const keyCloakSignIn = async (page: Page, browserName: string) => {
const login = `user-e2e-${browserName}`;
const password = `password-e2e-${browserName}`;
await expect(
page.locator('.login-pf-page-header').getByText('impress'),
).toBeVisible();
if (await page.getByLabel('Restart login').isVisible()) {
await page.getByRole('textbox', { name: 'password' }).fill(password);
await page.click('input[type="submit"]', { force: true });
} else {
await page.getByRole('textbox', { name: 'username' }).fill(login);
await page.getByRole('textbox', { name: 'password' }).fill(password);
await page.click('input[type="submit"]', { force: true });
await page.getByLabel('Restart login').click();
}
await page.getByRole('textbox', { name: 'username' }).fill(login);
await page.getByRole('textbox', { name: 'password' }).fill(password);
await page.click('input[type="submit"]', { force: true });
};
export const randomName = (name: string, browserName: string, length: number) =>
@@ -27,7 +27,6 @@ export const createDoc = async (
docName: string,
browserName: string,
length: number,
isPublic: boolean = false,
) => {
const randomDocs = randomName(docName, browserName, length);
@@ -44,22 +43,6 @@ export const createDoc = async (
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();
await page.getByText('Doc private').click();
await page.locator('.c__modal__backdrop').click({
position: { x: 0, y: 0 },
force: true,
});
await expect(
page
.getByLabel('It is the card information about the document.')
.getByText('Public'),
).toBeVisible();
}
}
return randomDocs;

View File

@@ -211,7 +211,11 @@ test.describe('Doc Header', () => {
const shareModal = page.getByLabel('Share modal');
await expect(shareModal.getByLabel('Doc private')).toBeEnabled();
await expect(
shareModal.getByRole('combobox', {
name: 'Visibility',
}),
).not.toHaveAttribute('disabled');
await expect(shareModal.getByText('Search by email')).toBeVisible();
const invitationCard = shareModal.getByLabel('List invitation card');
@@ -284,7 +288,11 @@ test.describe('Doc Header', () => {
const shareModal = page.getByLabel('Share modal');
await expect(shareModal.getByLabel('Doc private')).toBeDisabled();
await expect(
shareModal.getByRole('combobox', {
name: 'Visibility',
}),
).toHaveAttribute('disabled');
await expect(shareModal.getByText('Search by email')).toBeHidden();
const invitationCard = shareModal.getByLabel('List invitation card');
@@ -357,7 +365,11 @@ test.describe('Doc Header', () => {
const shareModal = page.getByLabel('Share modal');
await expect(shareModal.getByLabel('Doc private')).toBeDisabled();
await expect(
shareModal.getByRole('combobox', {
name: 'Visibility',
}),
).toHaveAttribute('disabled');
await expect(shareModal.getByText('Search by email')).toBeHidden();
const invitationCard = shareModal.getByLabel('List invitation card');

View File

@@ -164,14 +164,22 @@ test.describe('Document list members', () => {
const shareModal = page.getByLabel('Share modal');
// Admin still have the right to share
await expect(shareModal.getByLabel('Doc private')).toBeEnabled();
await expect(
shareModal.getByRole('combobox', {
name: 'Visibility',
}),
).not.toHaveAttribute('disabled');
await SelectRoleCurrentUser.click();
await page.getByRole('option', { name: 'Reader' }).click();
await expect(page.getByText('The role has been updated')).toBeVisible();
// Reader does not have the right to share
await expect(shareModal.getByLabel('Doc private')).toBeDisabled();
await expect(
shareModal.getByRole('combobox', {
name: 'Visibility',
}),
).toHaveAttribute('disabled');
});
test('it checks the delete members', async ({ page, browserName }) => {

View File

@@ -2,39 +2,13 @@ import { expect, test } from '@playwright/test';
import { createDoc, keyCloakSignIn } from './common';
const browsersName = ['chromium', 'webkit', 'firefox'];
test.describe('Doc Visibility', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/');
});
test('Make a public doc', async ({ page, browserName }) => {
const [docTitle] = await createDoc(
page,
'My new doc',
browserName,
1,
true,
);
const header = page.locator('header').first();
await header.locator('h2').getByText('Docs').click();
const datagrid = page.getByLabel('Datagrid of the documents page 1');
const datagridTable = datagrid.getByRole('table');
await expect(datagrid.getByLabel('Loading data')).toBeHidden({
timeout: 10000,
});
await expect(datagridTable.getByText(docTitle)).toBeVisible();
const row = datagridTable.getByRole('row').filter({
hasText: docTitle,
});
await expect(row.getByRole('cell').nth(0)).toHaveText('Public');
});
test('It checks the copy link button', async ({ page, browserName }) => {
// eslint-disable-next-line playwright/no-skipped-test
test.skip(
@@ -56,12 +30,48 @@ test.describe('Doc Visibility', () => {
expect(clipboardContent).toMatch(page.url());
});
test('It checks the link role options', async ({ page, browserName }) => {
await createDoc(page, 'Doc role options', browserName, 1);
await page.getByRole('button', { name: 'Share' }).click();
const selectVisibility = page.getByRole('combobox', {
name: 'Visibility',
});
await expect(selectVisibility.getByText('Authenticated')).toBeVisible();
await expect(page.getByLabel('Read only')).toBeVisible();
await expect(page.getByLabel('Can read and edit')).toBeVisible();
await selectVisibility.click();
await page
.getByRole('option', {
name: 'Restricted',
})
.click();
await expect(page.getByLabel('Read only')).toBeHidden();
await expect(page.getByLabel('Can read and edit')).toBeHidden();
await selectVisibility.click();
await page
.getByRole('option', {
name: 'Public',
})
.click();
await expect(page.getByLabel('Read only')).toBeVisible();
await expect(page.getByLabel('Can read and edit')).toBeVisible();
});
});
test.describe('Doc Visibility: Not loggued', () => {
test.describe('Doc Visibility: Restricted', () => {
test.use({ storageState: { cookies: [], origins: [] } });
test('A public doc is accessible even when not authentified.', async ({
test('A doc is not accessible when not authentified.', async ({
page,
browserName,
}) => {
@@ -70,14 +80,211 @@ test.describe('Doc Visibility: Not loggued', () => {
const [docTitle] = await createDoc(
page,
'My new doc',
'Restricted no auth',
browserName,
1,
true,
);
await expect(page.getByRole('heading', { name: docTitle })).toBeVisible();
await page.getByRole('button', { name: 'Share' }).click();
await page
.getByRole('combobox', {
name: 'Visibility',
})
.click();
await page
.getByRole('option', {
name: 'Restricted',
})
.click();
await expect(
page.getByText('The document visiblitity has been updated.'),
page.getByText('The document visibility has been updated.'),
).toBeVisible();
await page.locator('.c__modal__backdrop').click({
position: { x: 0, y: 0 },
});
const urlDoc = page.url();
await page
.getByRole('button', {
name: 'Logout',
})
.click();
await expect(page.getByRole('button', { name: 'Sign in' })).toBeVisible();
await page.goto(urlDoc);
await expect(page.getByRole('textbox', { name: 'password' })).toBeVisible();
});
test('A doc is not accessible when authentified but not member.', async ({
page,
browserName,
}) => {
await page.goto('/');
await keyCloakSignIn(page, browserName);
const [docTitle] = await createDoc(page, 'Restricted auth', browserName, 1);
await expect(page.getByRole('heading', { name: docTitle })).toBeVisible();
await page.getByRole('button', { name: 'Share' }).click();
await page
.getByRole('combobox', {
name: 'Visibility',
})
.click();
await page
.getByRole('option', {
name: 'Restricted',
})
.click();
await expect(
page.getByText('The document visibility has been updated.'),
).toBeVisible();
await page.locator('.c__modal__backdrop').click({
position: { x: 0, y: 0 },
});
const urlDoc = page.url();
await page
.getByRole('button', {
name: 'Logout',
})
.click();
const otherBrowser = browsersName.find((b) => b !== browserName);
await keyCloakSignIn(page, otherBrowser!);
await page.goto(urlDoc);
await expect(
page.getByText('You do not have permission to perform this action.'),
).toBeVisible();
});
test('A doc is accessible when member.', async ({ page, browserName }) => {
test.slow();
await page.goto('/');
await keyCloakSignIn(page, browserName);
const [docTitle] = await createDoc(page, 'Restricted auth', browserName, 1);
await expect(page.getByRole('heading', { name: docTitle })).toBeVisible();
await page.getByRole('button', { name: 'Share' }).click();
await page
.getByRole('combobox', {
name: 'Visibility',
})
.click();
await page
.getByRole('option', {
name: 'Restricted',
})
.click();
await expect(
page.getByText('The document visibility has been updated.'),
).toBeVisible();
const inputSearch = page.getByLabel(/Find a member to add to the document/);
const otherBrowser = browsersName.find((b) => b !== browserName);
const username = `user@${otherBrowser}.e2e`;
await inputSearch.fill(username);
await page.getByRole('option', { name: username }).click();
// Choose a role
await page.getByRole('combobox', { name: /Choose a role/ }).click();
await page.getByRole('option', { name: 'Administrator' }).click();
await page.getByRole('button', { name: 'Validate' }).click();
await expect(
page.getByText(`User ${username} added to the document.`),
).toBeVisible();
await page.locator('.c__modal__backdrop').click({
position: { x: 0, y: 0 },
});
const urlDoc = page.url();
await page
.getByRole('button', {
name: 'Logout',
})
.click();
await keyCloakSignIn(page, otherBrowser!);
await page.goto(urlDoc);
await expect(page.locator('h2').getByText(docTitle)).toBeVisible();
await expect(page.getByRole('button', { name: 'Share' })).toBeVisible();
});
});
test.describe('Doc Visibility: Public', () => {
test.use({ storageState: { cookies: [], origins: [] } });
test('It checks a public doc in read only mode', async ({
page,
browserName,
}) => {
await page.goto('/');
await keyCloakSignIn(page, browserName);
const [docTitle] = await createDoc(
page,
'Public read only',
browserName,
1,
);
await expect(page.getByRole('heading', { name: docTitle })).toBeVisible();
await page.getByRole('button', { name: 'Share' }).click();
await page
.getByRole('combobox', {
name: 'Visibility',
})
.click();
await page
.getByRole('option', {
name: 'Public',
})
.click();
await expect(
page.getByText('The document visibility has been updated.'),
).toBeVisible();
await page.getByLabel('Read only').click();
await expect(
page.getByText('The document visibility has been updated.').first(),
).toBeVisible();
await page.locator('.c__modal__backdrop').click({
position: { x: 0, y: 0 },
});
await expect(
page
.getByLabel('It is the card information about the document.')
.getByText('Public', { exact: true }),
).toBeVisible();
const urlDoc = page.url();
@@ -94,19 +301,54 @@ test.describe('Doc Visibility: Not loggued', () => {
await expect(page.locator('h2').getByText(docTitle)).toBeVisible();
await expect(page.getByRole('button', { name: 'Share' })).toBeHidden();
await expect(
page.getByText('Read only, you cannot edit this document'),
).toBeVisible();
});
test('A private doc redirect to the OIDC when not authentified.', async ({
test('It checks a public doc in editable mode', async ({
page,
browserName,
}) => {
test.slow();
await page.goto('/');
await keyCloakSignIn(page, browserName);
const [docTitle] = await createDoc(page, 'My private doc', browserName, 1);
const [docTitle] = await createDoc(page, 'Public editable', browserName, 1);
await expect(page.locator('h2').getByText(docTitle)).toBeVisible();
await expect(page.getByRole('heading', { name: docTitle })).toBeVisible();
await page.getByRole('button', { name: 'Share' }).click();
await page
.getByRole('combobox', {
name: 'Visibility',
})
.click();
await page
.getByRole('option', {
name: 'Public',
})
.click();
await expect(
page.getByText('The document visibility has been updated.'),
).toBeVisible();
await page.getByLabel('Can read and edit').click();
await expect(
page.getByText('The document visibility has been updated.').first(),
).toBeVisible();
await page.locator('.c__modal__backdrop').click({
position: { x: 0, y: 0 },
});
await expect(
page
.getByLabel('It is the card information about the document.')
.getByText('Public', { exact: true }),
).toBeVisible();
const urlDoc = page.url();
@@ -116,10 +358,134 @@ test.describe('Doc Visibility: Not loggued', () => {
})
.click();
await expect(page.getByRole('textbox', { name: 'password' })).toBeVisible();
await expect(page.getByRole('button', { name: 'Sign in' })).toBeVisible();
await page.goto(urlDoc);
await expect(page.getByRole('textbox', { name: 'password' })).toBeVisible();
await expect(page.locator('h2').getByText(docTitle)).toBeVisible();
await expect(page.getByRole('button', { name: 'Share' })).toBeHidden();
await expect(
page.getByText('Read only, you cannot edit this document'),
).toBeHidden();
});
});
test.describe('Doc Visibility: Authenticated', () => {
test.use({ storageState: { cookies: [], origins: [] } });
test('A doc is not accessible when unauthentified.', async ({
page,
browserName,
}) => {
await page.goto('/');
await keyCloakSignIn(page, browserName);
const [docTitle] = await createDoc(
page,
'Authenticated unauthentified',
browserName,
1,
);
await expect(page.getByRole('heading', { name: docTitle })).toBeVisible();
const urlDoc = page.url();
await page
.getByRole('button', {
name: 'Logout',
})
.click();
await expect(page.getByRole('button', { name: 'Sign in' })).toBeVisible();
await page.goto(urlDoc);
await expect(page.locator('h2').getByText(docTitle)).toBeHidden();
await expect(page.getByRole('textbox', { name: 'password' })).toBeVisible();
});
test('It checks a authenticated doc in read only mode', async ({
page,
browserName,
}) => {
await page.goto('/');
await keyCloakSignIn(page, browserName);
const [docTitle] = await createDoc(
page,
'Authenticated read only',
browserName,
1,
);
await expect(page.getByRole('heading', { name: docTitle })).toBeVisible();
const urlDoc = page.url();
await page
.getByRole('button', {
name: 'Logout',
})
.click();
const otherBrowser = browsersName.find((b) => b !== browserName);
await keyCloakSignIn(page, otherBrowser!);
await page.goto(urlDoc);
await expect(page.locator('h2').getByText(docTitle)).toBeVisible();
await expect(page.getByRole('button', { name: 'Share' })).toBeVisible();
await expect(
page.getByText('Read only, you cannot edit this document'),
).toBeVisible();
});
test('It checks a authenticated doc in editable mode', async ({
page,
browserName,
}) => {
await page.goto('/');
await keyCloakSignIn(page, browserName);
const [docTitle] = await createDoc(
page,
'Authenticated editable',
browserName,
1,
);
await expect(page.getByRole('heading', { name: docTitle })).toBeVisible();
const urlDoc = page.url();
await page.getByRole('button', { name: 'Share' }).click();
await page.getByLabel('Can read and edit').click();
await expect(
page.getByText('The document visibility has been updated.').first(),
).toBeVisible();
await page.locator('.c__modal__backdrop').click({
position: { x: 0, y: 0 },
});
await page
.getByRole('button', {
name: 'Logout',
})
.click();
const otherBrowser = browsersName.find((b) => b !== browserName);
await keyCloakSignIn(page, otherBrowser!);
await page.goto(urlDoc);
await expect(page.locator('h2').getByText(docTitle)).toBeVisible();
await expect(page.getByRole('button', { name: 'Share' })).toBeVisible();
await expect(
page.getByText('Read only, you cannot edit this document'),
).toBeHidden();
});
});

View File

@@ -358,6 +358,8 @@ const config = {
},
'forms-field': {
color: 'var(--c--theme--colors--primary-text)',
'footer-font-size': 'var(--c--theme--font--sizes--t)',
'footer-color': 'var(--c--theme--colors--greyscale-text)',
},
'forms-input': {
'border-radius': '4px',
@@ -372,6 +374,9 @@ const config = {
big: 'var(--c--theme--colors--primary-text)',
},
},
'forms-radio': {
'accent-color': 'var(--c--theme--colors--primary-600)',
},
'forms-select': {
'item-font-size': '14px',
'border-radius': '4px',

View File

@@ -16,6 +16,12 @@
line-height: initial;
}
.c__field .c__field__footer {
padding: 2px 0 0;
font-size: var(--c--components--forms-field--footer-font-size);
color: var(--c--components--forms-field--footer-color);
}
.labelled-box label {
color: var(--c--theme--colors--primary-text);
}
@@ -328,6 +334,10 @@ input:-webkit-autofill:focus {
cursor: not-allowed;
}
.c__checkbox.c__checkbox--disabled .c__checkbox__label {
color: var(--c--theme--colors--greyscale-400);
}
/**
* Button
*/

View File

@@ -477,6 +477,12 @@
--c--components--forms-datepicker--border-radius: 0;
--c--components--forms-fileuploader--border-radius: 0;
--c--components--forms-field--color: var(--c--theme--colors--primary-text);
--c--components--forms-field--footer-font-size: var(
--c--theme--font--sizes--t
);
--c--components--forms-field--footer-color: var(
--c--theme--colors--greyscale-text
);
--c--components--forms-input--border-radius: 4px;
--c--components--forms-input--background-color: #fff;
--c--components--forms-input--border-color: var(
@@ -492,6 +498,9 @@
--c--components--forms-labelledbox--label-color--big: var(
--c--theme--colors--primary-text
);
--c--components--forms-radio--accent-color: var(
--c--theme--colors--primary-600
);
--c--components--forms-select--item-font-size: 14px;
--c--components--forms-select--border-radius: 4px;
--c--components--forms-select--border-radius-hover: 4px;

View File

@@ -479,7 +479,11 @@ export const tokens = {
},
'forms-datepicker': { 'border-radius': '0' },
'forms-fileuploader': { 'border-radius': '0' },
'forms-field': { color: 'var(--c--theme--colors--primary-text)' },
'forms-field': {
color: 'var(--c--theme--colors--primary-text)',
'footer-font-size': 'var(--c--theme--font--sizes--t)',
'footer-color': 'var(--c--theme--colors--greyscale-text)',
},
'forms-input': {
'border-radius': '4px',
'background-color': '#ffffff',
@@ -491,6 +495,9 @@ export const tokens = {
'forms-labelledbox': {
'label-color': { big: 'var(--c--theme--colors--primary-text)' },
},
'forms-radio': {
'accent-color': 'var(--c--theme--colors--primary-600)',
},
'forms-select': {
'item-font-size': '14px',
'border-radius': '4px',

View File

@@ -1,16 +1,17 @@
import {
Button,
Switch,
Radio,
RadioGroup,
Select,
VariantType,
useToastProvider,
} from '@openfun/cunningham-react';
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Box, Card, IconBG } from '@/components';
import { useCunninghamTheme } from '@/cunningham';
import { KEY_DOC, KEY_LIST_DOC, useUpdateDocLink } from '../api';
import { Doc, LinkReach } from '../types';
import { Doc, LinkReach, LinkRole } from '../types';
interface DocVisibilityProps {
doc: Doc;
@@ -18,14 +19,12 @@ interface DocVisibilityProps {
export const DocVisibility = ({ doc }: DocVisibilityProps) => {
const { t } = useTranslation();
const [docPublic, setDocPublic] = useState(
doc.link_reach === LinkReach.PUBLIC,
);
const { toast } = useToastProvider();
const { colorsTokens } = useCunninghamTheme();
const api = useUpdateDocLink({
onSuccess: () => {
toast(
t('The document visiblitity has been updated.'),
t('The document visibility has been updated.'),
VariantType.SUCCESS,
{
duration: 4000,
@@ -35,6 +34,34 @@ export const DocVisibility = ({ doc }: DocVisibilityProps) => {
listInvalideQueries: [KEY_LIST_DOC, KEY_DOC],
});
const transLinkReach = {
[LinkReach.RESTRICTED]: {
label: t('Restricted'),
description: t('Only for people with access'),
},
[LinkReach.AUTHENTICATED]: {
label: t('Authenticated'),
description: t('Only for authenticated users'),
},
[LinkReach.PUBLIC]: {
label: t('Public'),
description: t('Anyone on the internet with the link can view'),
},
};
const linkRoleList = [
{
label: t('Read only'),
value: LinkRole.READER,
},
{
label: t('Can read and edit'),
value: LinkRole.EDITOR,
},
];
const showLinkRoleOptions = doc.link_reach !== LinkReach.RESTRICTED;
return (
<Card
$margin="tiny"
@@ -44,55 +71,73 @@ export const DocVisibility = ({ doc }: DocVisibilityProps) => {
$align="center"
$justify="space-between"
$gap="1rem"
$wrap="wrap"
>
<IconBG iconName="public" $margin="none" />
<IconBG iconName="public" />
<Box
$width="100%"
$wrap="wrap"
$gap="1rem"
$justify="space-between"
$direction="row"
$align="center"
$flex="1"
$css={`
& .c__field__footer .c__field__text {
${!doc.abilities.link_configuration && `color: ${colorsTokens()['greyscale-400']};`};
}
`}
>
<Box $direction="row" $gap="1rem" $align="center">
<Switch
label={t(docPublic ? 'Doc public' : 'Doc private')}
defaultChecked={docPublic}
onChange={() => {
<Box $shrink="0" $flex="auto" $maxWidth="20rem">
<Select
label={t('Visibility')}
options={Object.values(LinkReach).map((linkReach) => ({
label: transLinkReach[linkReach].label,
value: linkReach,
}))}
onChange={(evt) =>
api.mutate({
link_reach: evt.target.value as LinkReach,
id: doc.id,
link_reach: docPublic ? LinkReach.RESTRICTED : LinkReach.PUBLIC,
link_role: 'reader',
});
setDocPublic(!docPublic);
}}
disabled={!doc.abilities.link_configuration}
text={
docPublic
? t('Anyone on the internet with the link can view')
: t('Only for people with access')
})
}
value={doc.link_reach}
clearable={false}
text={transLinkReach[doc.link_reach].description}
disabled={!doc.abilities.link_configuration}
/>
</Box>
<Button
onClick={() => {
navigator.clipboard
.writeText(window.location.href)
.then(() => {
toast(t('Link Copied !'), VariantType.SUCCESS, {
duration: 3000,
});
})
.catch(() => {
toast(t('Failed to copy link'), VariantType.ERROR, {
duration: 3000,
});
});
}}
color="primary"
icon={<span className="material-icons">copy</span>}
>
{t('Copy link')}
</Button>
{showLinkRoleOptions && (
<Box
$css={`
& .c__checkbox{
padding: 0.15rem 0.25rem;
}
`}
>
<RadioGroup
compact
style={{
display: 'flex',
}}
text={t('How people can interact with the document')}
>
{linkRoleList.map((radio) => (
<Radio
key={radio.value}
label={radio.label}
value={radio.value}
onChange={() =>
api.mutate({
link_role: radio.value,
id: doc.id,
})
}
checked={doc.link_role === radio.value}
disabled={!doc.abilities.link_configuration}
/>
))}
</RadioGroup>
</Box>
)}
</Box>
</Card>
);

View File

@@ -1,3 +1,8 @@
import {
Button,
VariantType,
useToastProvider,
} from '@openfun/cunningham-react';
import { t } from 'i18next';
import { createGlobalStyle } from 'styled-components';
@@ -41,6 +46,7 @@ interface ModalShareProps {
export const ModalShare = ({ onClose, doc }: ModalShareProps) => {
const { isMobile, isSmallMobile } = useResponsiveStore();
const width = isSmallMobile ? '100vw' : isMobile ? '90vw' : '70vw';
const { toast } = useToastProvider();
return (
<>
@@ -68,13 +74,44 @@ export const ModalShare = ({ onClose, doc }: ModalShareProps) => {
iconName="share"
$margin="none"
/>
<Box $align="flex-start">
<Text as="h3" $size="26px" $margin="none">
{t('Share')}
</Text>
<Text $size="small" $weight="normal" $textAlign="left">
{doc.title}
</Text>
<Box
$justify="space-between"
$direction="row"
$align="center"
$width="100%"
$gap="1rem"
$wrap="wrap"
>
<Box $align="flex-start">
<Text as="h3" $size="26px" $margin="none">
{t('Share')}
</Text>
<Text $size="small" $weight="normal" $textAlign="left">
{doc.title}
</Text>
</Box>
<Box $margin={{ right: '1.5rem' }} $shrink="0">
<Button
onClick={() => {
navigator.clipboard
.writeText(window.location.href)
.then(() => {
toast(t('Link Copied !'), VariantType.SUCCESS, {
duration: 3000,
});
})
.catch(() => {
toast(t('Failed to copy link'), VariantType.ERROR, {
duration: 3000,
});
});
}}
color="primary"
icon={<span className="material-icons">copy</span>}
>
{t('Copy link')}
</Button>
</Box>
</Box>
</Card>
<DocVisibility doc={doc} />

View File

@@ -27,6 +27,11 @@ export enum LinkReach {
AUTHENTICATED = 'authenticated',
}
export enum LinkRole {
READER = 'reader',
EDITOR = 'editor',
}
export type Base64 = string;
export interface Doc {
@@ -34,7 +39,7 @@ export interface Doc {
title: string;
content: Base64;
link_reach: LinkReach;
link_role: 'reader' | 'editor';
link_role: LinkRole;
accesses: Access[];
created_at: string;
updated_at: string;

View File

@@ -1,7 +1,11 @@
import { WorkboxPlugin } from 'workbox-core';
import { Doc, DocsResponse } from '@/features/docs/doc-management';
import { LinkReach, Role } from '@/features/docs/doc-management/types';
import {
LinkReach,
LinkRole,
Role,
} from '@/features/docs/doc-management/types';
import { DBRequest, DocsDB } from './DocsDB';
import { RequestSerializer } from './RequestSerializer';
@@ -220,7 +224,7 @@ export class ApiPlugin implements WorkboxPlugin {
},
],
link_reach: LinkReach.RESTRICTED,
link_role: 'reader',
link_role: LinkRole.READER,
};
await DocsDB.cacheResponse(