diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4db20e21..5d70a16e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,7 @@ and this project adheres to
- 📝Contributing.md #352
- 🌐(frontend) add localization to editor #268
+- ✨Public and restricted doc editable #357
## Fixed
diff --git a/src/frontend/apps/e2e/__tests__/app-impress/common.ts b/src/frontend/apps/e2e/__tests__/app-impress/common.ts
index 4d5c43c4..2c52eeee 100644
--- a/src/frontend/apps/e2e/__tests__/app-impress/common.ts
+++ b/src/frontend/apps/e2e/__tests__/app-impress/common.ts
@@ -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;
diff --git a/src/frontend/apps/e2e/__tests__/app-impress/doc-header.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/doc-header.spec.ts
index 6c9b59d1..a75c4593 100644
--- a/src/frontend/apps/e2e/__tests__/app-impress/doc-header.spec.ts
+++ b/src/frontend/apps/e2e/__tests__/app-impress/doc-header.spec.ts
@@ -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');
diff --git a/src/frontend/apps/e2e/__tests__/app-impress/doc-member-list.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/doc-member-list.spec.ts
index e9bfb78f..f06c66fc 100644
--- a/src/frontend/apps/e2e/__tests__/app-impress/doc-member-list.spec.ts
+++ b/src/frontend/apps/e2e/__tests__/app-impress/doc-member-list.spec.ts
@@ -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 }) => {
diff --git a/src/frontend/apps/e2e/__tests__/app-impress/doc-visibility.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/doc-visibility.spec.ts
index 759f5627..064d7a93 100644
--- a/src/frontend/apps/e2e/__tests__/app-impress/doc-visibility.spec.ts
+++ b/src/frontend/apps/e2e/__tests__/app-impress/doc-visibility.spec.ts
@@ -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();
});
});
diff --git a/src/frontend/apps/impress/cunningham.ts b/src/frontend/apps/impress/cunningham.ts
index 00f3c66a..1990d492 100644
--- a/src/frontend/apps/impress/cunningham.ts
+++ b/src/frontend/apps/impress/cunningham.ts
@@ -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',
diff --git a/src/frontend/apps/impress/src/cunningham/cunningham-style.css b/src/frontend/apps/impress/src/cunningham/cunningham-style.css
index 68dca6eb..3250fca1 100644
--- a/src/frontend/apps/impress/src/cunningham/cunningham-style.css
+++ b/src/frontend/apps/impress/src/cunningham/cunningham-style.css
@@ -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
*/
diff --git a/src/frontend/apps/impress/src/cunningham/cunningham-tokens.css b/src/frontend/apps/impress/src/cunningham/cunningham-tokens.css
index b30e3507..3d87ac17 100644
--- a/src/frontend/apps/impress/src/cunningham/cunningham-tokens.css
+++ b/src/frontend/apps/impress/src/cunningham/cunningham-tokens.css
@@ -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;
diff --git a/src/frontend/apps/impress/src/cunningham/cunningham-tokens.ts b/src/frontend/apps/impress/src/cunningham/cunningham-tokens.ts
index d435b859..4e6bfdda 100644
--- a/src/frontend/apps/impress/src/cunningham/cunningham-tokens.ts
+++ b/src/frontend/apps/impress/src/cunningham/cunningham-tokens.ts
@@ -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',
diff --git a/src/frontend/apps/impress/src/features/docs/doc-management/components/DocVisibility.tsx b/src/frontend/apps/impress/src/features/docs/doc-management/components/DocVisibility.tsx
index 8d7f9628..fb0da949 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-management/components/DocVisibility.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-management/components/DocVisibility.tsx
@@ -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 (
{
$align="center"
$justify="space-between"
$gap="1rem"
+ $wrap="wrap"
>
-
+
-
- {
+
+
-
+ {showLinkRoleOptions && (
+
+
+ {linkRoleList.map((radio) => (
+
+ api.mutate({
+ link_role: radio.value,
+ id: doc.id,
+ })
+ }
+ checked={doc.link_role === radio.value}
+ disabled={!doc.abilities.link_configuration}
+ />
+ ))}
+
+
+ )}
);
diff --git a/src/frontend/apps/impress/src/features/docs/doc-management/components/ModalShare.tsx b/src/frontend/apps/impress/src/features/docs/doc-management/components/ModalShare.tsx
index 9357c3cd..3c8807bc 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-management/components/ModalShare.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-management/components/ModalShare.tsx
@@ -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"
/>
-
-
- {t('Share')}
-
-
- {doc.title}
-
+
+
+
+ {t('Share')}
+
+
+ {doc.title}
+
+
+
+
+
diff --git a/src/frontend/apps/impress/src/features/docs/doc-management/types.tsx b/src/frontend/apps/impress/src/features/docs/doc-management/types.tsx
index 2e616983..8a39e09f 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-management/types.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-management/types.tsx
@@ -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;
diff --git a/src/frontend/apps/impress/src/features/service-worker/ApiPlugin.ts b/src/frontend/apps/impress/src/features/service-worker/ApiPlugin.ts
index 9d3c441b..76706fe4 100644
--- a/src/frontend/apps/impress/src/features/service-worker/ApiPlugin.ts
+++ b/src/frontend/apps/impress/src/features/service-worker/ApiPlugin.ts
@@ -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(