✨(frontend) button access request on share modal
When a document is in public or connected mode, users can now request access to the document.
This commit is contained in:
committed by
Manuel Raynaud
parent
2360a832af
commit
388f71d9d0
@@ -1,14 +1,13 @@
|
||||
import { expect, test } from '@playwright/test';
|
||||
|
||||
import {
|
||||
BROWSERS,
|
||||
createDoc,
|
||||
expectLoginPage,
|
||||
keyCloakSignIn,
|
||||
verifyDocName,
|
||||
} from './common';
|
||||
|
||||
const browsersName = ['chromium', 'webkit', 'firefox'];
|
||||
|
||||
test.describe('Doc Visibility', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/');
|
||||
@@ -118,18 +117,20 @@ test.describe('Doc Visibility: Restricted', () => {
|
||||
})
|
||||
.click();
|
||||
|
||||
const otherBrowser = browsersName.find((b) => b !== browserName);
|
||||
const otherBrowser = BROWSERS.find((b) => b !== browserName);
|
||||
|
||||
await keyCloakSignIn(page, otherBrowser!);
|
||||
|
||||
await expect(
|
||||
page.getByRole('link', { name: 'Docs Logo Docs' }),
|
||||
).toBeVisible();
|
||||
).toBeVisible({
|
||||
timeout: 10000,
|
||||
});
|
||||
|
||||
await page.goto(urlDoc);
|
||||
|
||||
await expect(
|
||||
page.getByText('You do not have permission to view this document.'),
|
||||
page.getByText('Insufficient access rights to view the document.'),
|
||||
).toBeVisible({
|
||||
timeout: 10000,
|
||||
});
|
||||
@@ -150,7 +151,7 @@ test.describe('Doc Visibility: Restricted', () => {
|
||||
name: 'Quick search input',
|
||||
});
|
||||
|
||||
const otherBrowser = browsersName.find((b) => b !== browserName);
|
||||
const otherBrowser = BROWSERS.find((b) => b !== browserName);
|
||||
const username = `user@${otherBrowser}.test`;
|
||||
await inputSearch.fill(username);
|
||||
await page.getByRole('option', { name: username }).click();
|
||||
@@ -262,11 +263,20 @@ test.describe('Doc Visibility: Public', () => {
|
||||
await expect(page.locator('h2').getByText(docTitle)).toBeVisible();
|
||||
await expect(page.getByRole('button', { name: 'search' })).toBeHidden();
|
||||
await expect(page.getByRole('button', { name: 'New doc' })).toBeHidden();
|
||||
await expect(page.getByRole('button', { name: 'Share' })).toBeVisible();
|
||||
const card = page.getByLabel('It is the card information');
|
||||
await expect(card).toBeVisible();
|
||||
|
||||
await expect(card.getByText('Reader')).toBeVisible();
|
||||
|
||||
await page.getByRole('button', { name: 'Share' }).click();
|
||||
await expect(
|
||||
page.getByText(
|
||||
'You do not have permission to view users sharing this document or modify link settings.',
|
||||
),
|
||||
).toBeVisible();
|
||||
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Request access' }),
|
||||
).toBeHidden();
|
||||
});
|
||||
|
||||
test('It checks a public doc in editable mode', async ({
|
||||
@@ -430,7 +440,7 @@ test.describe('Doc Visibility: Authenticated', () => {
|
||||
})
|
||||
.click();
|
||||
|
||||
const otherBrowser = browsersName.find((b) => b !== browserName);
|
||||
const otherBrowser = BROWSERS.find((b) => b !== browserName);
|
||||
await keyCloakSignIn(page, otherBrowser!);
|
||||
|
||||
await expect(
|
||||
@@ -443,6 +453,18 @@ test.describe('Doc Visibility: Authenticated', () => {
|
||||
await page.getByRole('button', { name: 'Share' }).click();
|
||||
await page.getByRole('button', { name: 'Copy link' }).click();
|
||||
await expect(page.getByText('Link Copied !')).toBeVisible();
|
||||
|
||||
await expect(
|
||||
page.getByText(
|
||||
'You do not have permission to view users sharing this document or modify link settings.',
|
||||
),
|
||||
).toBeVisible();
|
||||
|
||||
await page.getByRole('button', { name: 'Request access' }).click();
|
||||
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Request access' }),
|
||||
).toBeDisabled();
|
||||
});
|
||||
|
||||
test('It checks a authenticated doc in editable mode', async ({
|
||||
@@ -490,7 +512,7 @@ test.describe('Doc Visibility: Authenticated', () => {
|
||||
})
|
||||
.click();
|
||||
|
||||
const otherBrowser = browsersName.find((b) => b !== browserName);
|
||||
const otherBrowser = BROWSERS.find((b) => b !== browserName);
|
||||
await keyCloakSignIn(page, otherBrowser!);
|
||||
|
||||
await expect(
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {
|
||||
Button,
|
||||
ButtonProps,
|
||||
VariantType,
|
||||
useToastProvider,
|
||||
} from '@openfun/cunningham-react';
|
||||
@@ -11,10 +12,13 @@ import { Box, BoxButton, Icon, LoadMoreText } from '@/components';
|
||||
import { QuickSearchData, QuickSearchGroup } from '@/components/quick-search';
|
||||
import { useCunninghamTheme } from '@/cunningham';
|
||||
import { AccessRequest, Doc } from '@/docs/doc-management/';
|
||||
import { useAuth } from '@/features/auth';
|
||||
|
||||
import {
|
||||
useAcceptDocAccessRequest,
|
||||
useCreateDocAccessRequest,
|
||||
useDeleteDocAccessRequest,
|
||||
useDocAccessRequests,
|
||||
useDocAccessRequestsInfinite,
|
||||
} from '../api/useDocAccessRequest';
|
||||
|
||||
@@ -147,3 +151,45 @@ export const QuickSearchGroupAccessRequest = ({
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
type ButtonAccessRequestProps = {
|
||||
docId: Doc['id'];
|
||||
} & ButtonProps;
|
||||
|
||||
export const ButtonAccessRequest = ({
|
||||
docId,
|
||||
...buttonProps
|
||||
}: ButtonAccessRequestProps) => {
|
||||
const { authenticated } = useAuth();
|
||||
const { data: requests } = useDocAccessRequests({
|
||||
docId,
|
||||
page: 1,
|
||||
});
|
||||
const { t } = useTranslation();
|
||||
const { toast } = useToastProvider();
|
||||
const { mutate: createRequest } = useCreateDocAccessRequest({
|
||||
onSuccess: () => {
|
||||
toast(t('Access request sent successfully.'), VariantType.SUCCESS, {
|
||||
duration: 3000,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const hasRequested = !!(
|
||||
requests && requests?.results.find((request) => request.document === docId)
|
||||
);
|
||||
|
||||
if (!authenticated) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Button
|
||||
onClick={() => createRequest({ docId })}
|
||||
disabled={hasRequested}
|
||||
{...buttonProps}
|
||||
>
|
||||
{buttonProps.children || t('Request access')}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -17,7 +17,10 @@ import { isValidEmail } from '@/utils';
|
||||
|
||||
import { KEY_LIST_USER, useUsers } from '../api';
|
||||
|
||||
import { QuickSearchGroupAccessRequest } from './DocShareAccessRequest';
|
||||
import {
|
||||
ButtonAccessRequest,
|
||||
QuickSearchGroupAccessRequest,
|
||||
} from './DocShareAccessRequest';
|
||||
import { DocShareAddMemberList } from './DocShareAddMemberList';
|
||||
import {
|
||||
DocShareModalInviteUserRow,
|
||||
@@ -151,7 +154,12 @@ export const DocShareModal = ({ doc, onClose }: Props) => {
|
||||
|
||||
<Box data-testid="doc-share-quick-search">
|
||||
{!canViewAccesses && (
|
||||
<Box $height={listHeight} $align="center" $justify="center">
|
||||
<Box
|
||||
$height={listHeight}
|
||||
$align="center"
|
||||
$justify="center"
|
||||
$gap="1rem"
|
||||
>
|
||||
<Text
|
||||
$maxWidth="320px"
|
||||
$textAlign="center"
|
||||
@@ -162,6 +170,11 @@ export const DocShareModal = ({ doc, onClose }: Props) => {
|
||||
'You do not have permission to view users sharing this document or modify link settings.',
|
||||
)}
|
||||
</Text>
|
||||
<ButtonAccessRequest
|
||||
docId={doc.id}
|
||||
color="tertiary"
|
||||
size="small"
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
{canViewAccesses && (
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
export * from './DocShareModal';
|
||||
export * from './DocShareAccessRequest';
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
import {
|
||||
Button,
|
||||
VariantType,
|
||||
useToastProvider,
|
||||
} from '@openfun/cunningham-react';
|
||||
import { Button } from '@openfun/cunningham-react';
|
||||
import Head from 'next/head';
|
||||
import Image from 'next/image';
|
||||
import { useRouter } from 'next/router';
|
||||
@@ -13,10 +9,8 @@ import img403 from '@/assets/icons/icon-403.png';
|
||||
import { Box, Icon, Loading, StyledLink, Text } from '@/components';
|
||||
import { DEFAULT_QUERY_RETRY } from '@/core';
|
||||
import { KEY_DOC, useDoc } from '@/features/docs';
|
||||
import {
|
||||
useCreateDocAccessRequest,
|
||||
useDocAccessRequests,
|
||||
} from '@/features/docs/doc-share/api/useDocAccessRequest';
|
||||
import { ButtonAccessRequest } from '@/features/docs/doc-share';
|
||||
import { useDocAccessRequests } from '@/features/docs/doc-share/api/useDocAccessRequest';
|
||||
import { MainLayout } from '@/layouts';
|
||||
import { NextPageWithLayout } from '@/types/next';
|
||||
|
||||
@@ -54,16 +48,9 @@ const DocPage403 = ({ id }: DocProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { data: requests, isLoading: isLoadingRequest } = useDocAccessRequests({
|
||||
docId: id,
|
||||
page: 1,
|
||||
});
|
||||
const { replace } = useRouter();
|
||||
const { toast } = useToastProvider();
|
||||
const { mutate: createRequest } = useCreateDocAccessRequest({
|
||||
onSuccess: () => {
|
||||
toast(t('Access request sent successfully.'), VariantType.SUCCESS, {
|
||||
duration: 3000,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const hasRequested = !!requests?.results.find(
|
||||
(request) => request.document === id,
|
||||
@@ -84,7 +71,7 @@ const DocPage403 = ({ id }: DocProps) => {
|
||||
},
|
||||
);
|
||||
|
||||
if (error?.status !== 403) {
|
||||
if (!isLoadingDoc && error?.status !== 403) {
|
||||
void replace(`/docs/${id}`);
|
||||
return <Loading />;
|
||||
}
|
||||
@@ -137,12 +124,7 @@ const DocPage403 = ({ id }: DocProps) => {
|
||||
{t('Home')}
|
||||
</StyledButton>
|
||||
</StyledLink>
|
||||
<Button
|
||||
onClick={() => createRequest({ docId: id })}
|
||||
disabled={hasRequested}
|
||||
>
|
||||
{t('Request access')}
|
||||
</Button>
|
||||
<ButtonAccessRequest docId={id} />
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
Reference in New Issue
Block a user