🎨(app-desk) add feature members
The feature teams is getting big, we extracted codes related to members to a new feature members.
This commit is contained in:
@@ -4,8 +4,8 @@ import fetchMock from 'fetch-mock';
|
|||||||
|
|
||||||
import { AppWrapper } from '@/tests/utils';
|
import { AppWrapper } from '@/tests/utils';
|
||||||
|
|
||||||
import { Access, Role } from '../api';
|
import { MemberAction } from '../components/MemberAction';
|
||||||
import { MemberAction } from '../components/Member/MemberAction';
|
import { Access, Role } from '../types';
|
||||||
|
|
||||||
const access: Access = {
|
const access: Access = {
|
||||||
id: '789',
|
id: '789',
|
||||||
@@ -5,8 +5,8 @@ import fetchMock from 'fetch-mock';
|
|||||||
|
|
||||||
import { AppWrapper } from '@/tests/utils';
|
import { AppWrapper } from '@/tests/utils';
|
||||||
|
|
||||||
import { Access, Role } from '../api';
|
import { MemberGrid } from '../components/MemberGrid';
|
||||||
import { MemberGrid } from '../components/Member/MemberGrid';
|
import { Access, Role } from '../types';
|
||||||
|
|
||||||
describe('MemberGrid', () => {
|
describe('MemberGrid', () => {
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@@ -6,8 +6,8 @@ import fetchMock from 'fetch-mock';
|
|||||||
import { useAuthStore } from '@/features/auth';
|
import { useAuthStore } from '@/features/auth';
|
||||||
import { AppWrapper } from '@/tests/utils';
|
import { AppWrapper } from '@/tests/utils';
|
||||||
|
|
||||||
import { Access, Role } from '../api';
|
import { ModalRole } from '../components/ModalRole';
|
||||||
import { ModalRole } from '../components/Member/ModalRole';
|
import { Access, Role } from '../types';
|
||||||
|
|
||||||
const toast = jest.fn();
|
const toast = jest.fn();
|
||||||
jest.mock('@openfun/cunningham-react', () => ({
|
jest.mock('@openfun/cunningham-react', () => ({
|
||||||
@@ -2,7 +2,7 @@ import { UseQueryOptions, useQuery } from '@tanstack/react-query';
|
|||||||
|
|
||||||
import { APIError, APIList, errorCauses, fetchAPI } from '@/api';
|
import { APIError, APIList, errorCauses, fetchAPI } from '@/api';
|
||||||
|
|
||||||
import { Access } from './types';
|
import { Access } from '../types';
|
||||||
|
|
||||||
export type TeamAccessesAPIParams = {
|
export type TeamAccessesAPIParams = {
|
||||||
page: number;
|
page: number;
|
||||||
@@ -5,9 +5,10 @@ import {
|
|||||||
} from '@tanstack/react-query';
|
} from '@tanstack/react-query';
|
||||||
|
|
||||||
import { APIError, errorCauses, fetchAPI } from '@/api';
|
import { APIError, errorCauses, fetchAPI } from '@/api';
|
||||||
|
import { KEY_TEAM } from '@/features/teams/api/useTeam';
|
||||||
|
|
||||||
|
import { Access, Role } from '../types';
|
||||||
|
|
||||||
import { Access, Role } from './types';
|
|
||||||
import { KEY_TEAM } from './useTeam';
|
|
||||||
import { KEY_LIST_TEAM_ACCESSES } from './useTeamsAccesses';
|
import { KEY_LIST_TEAM_ACCESSES } from './useTeamsAccesses';
|
||||||
|
|
||||||
interface UpdateTeamAccessProps {
|
interface UpdateTeamAccessProps {
|
||||||
@@ -3,7 +3,8 @@ import React, { useState } from 'react';
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import { DropButton, Text } from '@/components';
|
import { DropButton, Text } from '@/components';
|
||||||
import { Access, Role } from '@/features/teams/api';
|
|
||||||
|
import { Access, Role } from '../types';
|
||||||
|
|
||||||
import { ModalRole } from './ModalRole';
|
import { ModalRole } from './ModalRole';
|
||||||
|
|
||||||
@@ -5,8 +5,10 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import IconUser from '@/assets/icons/icon-user.svg';
|
import IconUser from '@/assets/icons/icon-user.svg';
|
||||||
import { Box, Card, TextErrors } from '@/components';
|
import { Box, Card, TextErrors } from '@/components';
|
||||||
import { useCunninghamTheme } from '@/cunningham';
|
import { useCunninghamTheme } from '@/cunningham';
|
||||||
import { Role, useTeamAccesses } from '@/features/teams/api/';
|
|
||||||
import { PAGE_SIZE } from '@/features/teams/conf';
|
import { useTeamAccesses } from '../api/useTeamsAccesses';
|
||||||
|
import { PAGE_SIZE } from '../conf';
|
||||||
|
import { Role } from '../types';
|
||||||
|
|
||||||
import { MemberAction } from './MemberAction';
|
import { MemberAction } from './MemberAction';
|
||||||
|
|
||||||
@@ -12,7 +12,9 @@ import { useState } from 'react';
|
|||||||
|
|
||||||
import { Box, Text, TextErrors } from '@/components';
|
import { Box, Text, TextErrors } from '@/components';
|
||||||
import { useAuthStore } from '@/features/auth';
|
import { useAuthStore } from '@/features/auth';
|
||||||
import { Access, Role, useUpdateTeamAccess } from '@/features/teams/api/';
|
|
||||||
|
import { useUpdateTeamAccess } from '../api/useUpdateTeamAccess';
|
||||||
|
import { Access, Role } from '../types';
|
||||||
|
|
||||||
interface ModalRoleProps {
|
interface ModalRoleProps {
|
||||||
access: Access;
|
access: Access;
|
||||||
2
src/frontend/apps/desk/src/features/members/index.ts
Normal file
2
src/frontend/apps/desk/src/features/members/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export * from './types';
|
||||||
|
export * from './components/MemberGrid';
|
||||||
20
src/frontend/apps/desk/src/features/members/types.tsx
Normal file
20
src/frontend/apps/desk/src/features/members/types.tsx
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { User } from '@/features/auth/';
|
||||||
|
|
||||||
|
export enum Role {
|
||||||
|
MEMBER = 'member',
|
||||||
|
ADMIN = 'administrator',
|
||||||
|
OWNER = 'owner',
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Access {
|
||||||
|
id: string;
|
||||||
|
role: Role;
|
||||||
|
user: User;
|
||||||
|
abilities: {
|
||||||
|
delete: boolean;
|
||||||
|
get: boolean;
|
||||||
|
patch: boolean;
|
||||||
|
put: boolean;
|
||||||
|
set_role_to: Role[];
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -2,5 +2,3 @@ export * from './types';
|
|||||||
export * from './useCreateTeam';
|
export * from './useCreateTeam';
|
||||||
export * from './useTeam';
|
export * from './useTeam';
|
||||||
export * from './useTeams';
|
export * from './useTeams';
|
||||||
export * from './useTeamsAccesses';
|
|
||||||
export * from './useUpdateTeamAccess';
|
|
||||||
|
|||||||
@@ -1,23 +1,4 @@
|
|||||||
import { User } from '@/features/auth/';
|
import { Access } from '@/features/members';
|
||||||
|
|
||||||
export enum Role {
|
|
||||||
MEMBER = 'member',
|
|
||||||
ADMIN = 'administrator',
|
|
||||||
OWNER = 'owner',
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Access {
|
|
||||||
id: string;
|
|
||||||
role: Role;
|
|
||||||
user: User;
|
|
||||||
abilities: {
|
|
||||||
delete: boolean;
|
|
||||||
get: boolean;
|
|
||||||
patch: boolean;
|
|
||||||
put: boolean;
|
|
||||||
set_role_to: Role[];
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Team {
|
export interface Team {
|
||||||
id: string;
|
id: string;
|
||||||
|
|||||||
@@ -1,3 +1,2 @@
|
|||||||
export * from './Panel/Panel';
|
export * from './Panel/Panel';
|
||||||
export * from './TeamInfo';
|
export * from './TeamInfo';
|
||||||
export * from './Member/MemberGrid';
|
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ import { ReactElement } from 'react';
|
|||||||
|
|
||||||
import { Box } from '@/components';
|
import { Box } from '@/components';
|
||||||
import { TextErrors } from '@/components/TextErrors';
|
import { TextErrors } from '@/components/TextErrors';
|
||||||
import { MemberGrid, Role, TeamInfo, useTeam } from '@/features/teams/';
|
import { MemberGrid, Role } from '@/features/members';
|
||||||
|
import { TeamInfo, useTeam } from '@/features/teams/';
|
||||||
import { NextPageWithLayout } from '@/types/next';
|
import { NextPageWithLayout } from '@/types/next';
|
||||||
|
|
||||||
import TeamLayout from './TeamLayout';
|
import TeamLayout from './TeamLayout';
|
||||||
|
|||||||
65
src/frontend/apps/e2e/__tests__/app-desk/member-grid.spec.ts
Normal file
65
src/frontend/apps/e2e/__tests__/app-desk/member-grid.spec.ts
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
import { expect, test } from '@playwright/test';
|
||||||
|
|
||||||
|
import { createTeam, keyCloakSignIn } from './common';
|
||||||
|
|
||||||
|
test.beforeEach(async ({ page, browserName }) => {
|
||||||
|
await page.goto('/');
|
||||||
|
await keyCloakSignIn(page, browserName);
|
||||||
|
});
|
||||||
|
|
||||||
|
test.describe('Member Grid', () => {
|
||||||
|
test('checks the owner member is displayed correctly', async ({
|
||||||
|
page,
|
||||||
|
browserName,
|
||||||
|
}) => {
|
||||||
|
await createTeam(page, 'team-owner', browserName, 1);
|
||||||
|
|
||||||
|
const table = page.getByLabel('List members card').getByRole('table');
|
||||||
|
|
||||||
|
const thead = table.locator('thead');
|
||||||
|
await expect(thead.getByText(/Names/i)).toBeVisible();
|
||||||
|
await expect(thead.getByText(/Emails/i)).toBeVisible();
|
||||||
|
await expect(thead.getByText(/Roles/i)).toBeVisible();
|
||||||
|
|
||||||
|
const cells = table.getByRole('row').nth(1).getByRole('cell');
|
||||||
|
await expect(cells.nth(0).getByLabel('Member icon')).toBeVisible();
|
||||||
|
await expect(cells.nth(1)).toHaveText(
|
||||||
|
new RegExp(`E2E ${browserName}`, 'i'),
|
||||||
|
);
|
||||||
|
await expect(cells.nth(2)).toHaveText(`user@${browserName}.e2e`);
|
||||||
|
await expect(cells.nth(3)).toHaveText(/Owner/i);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('try to update the owner role but cannot because it is the last owner', async ({
|
||||||
|
page,
|
||||||
|
browserName,
|
||||||
|
}) => {
|
||||||
|
await createTeam(page, 'team-owner-role', browserName, 1);
|
||||||
|
|
||||||
|
const table = page.getByLabel('List members card').getByRole('table');
|
||||||
|
|
||||||
|
const cells = table.getByRole('row').nth(1).getByRole('cell');
|
||||||
|
await expect(cells.nth(1)).toHaveText(
|
||||||
|
new RegExp(`E2E ${browserName}`, 'i'),
|
||||||
|
);
|
||||||
|
await cells.nth(4).getByLabel('Member options').click();
|
||||||
|
await page.getByText('Update the role').click();
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.getByText('You are the last owner, you cannot change your role.'),
|
||||||
|
).toBeVisible();
|
||||||
|
|
||||||
|
const radioGroup = page.getByLabel('Radio buttons to update the roles');
|
||||||
|
|
||||||
|
const radios = await radioGroup.getByRole('radio').all();
|
||||||
|
for (const radio of radios) {
|
||||||
|
await expect(radio).toBeDisabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', {
|
||||||
|
name: 'Validate',
|
||||||
|
}),
|
||||||
|
).toBeDisabled();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -40,59 +40,4 @@ test.describe('Team', () => {
|
|||||||
page.getByText(`Last update at ${todayFormated}`),
|
page.getByText(`Last update at ${todayFormated}`),
|
||||||
).toBeVisible();
|
).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('checks the owner member is displayed correctly', async ({
|
|
||||||
page,
|
|
||||||
browserName,
|
|
||||||
}) => {
|
|
||||||
await createTeam(page, 'team-owner', browserName, 1);
|
|
||||||
|
|
||||||
const table = page.getByLabel('List members card').getByRole('table');
|
|
||||||
|
|
||||||
const thead = table.locator('thead');
|
|
||||||
await expect(thead.getByText(/Names/i)).toBeVisible();
|
|
||||||
await expect(thead.getByText(/Emails/i)).toBeVisible();
|
|
||||||
await expect(thead.getByText(/Roles/i)).toBeVisible();
|
|
||||||
|
|
||||||
const cells = table.getByRole('row').nth(1).getByRole('cell');
|
|
||||||
await expect(cells.nth(0).getByLabel('Member icon')).toBeVisible();
|
|
||||||
await expect(cells.nth(1)).toHaveText(
|
|
||||||
new RegExp(`E2E ${browserName}`, 'i'),
|
|
||||||
);
|
|
||||||
await expect(cells.nth(2)).toHaveText(`user@${browserName}.e2e`);
|
|
||||||
await expect(cells.nth(3)).toHaveText(/Owner/i);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('try to update the owner role but cannot because it is the last owner', async ({
|
|
||||||
page,
|
|
||||||
browserName,
|
|
||||||
}) => {
|
|
||||||
await createTeam(page, 'team-owner-role', browserName, 1);
|
|
||||||
|
|
||||||
const table = page.getByLabel('List members card').getByRole('table');
|
|
||||||
|
|
||||||
const cells = table.getByRole('row').nth(1).getByRole('cell');
|
|
||||||
await expect(cells.nth(1)).toHaveText(
|
|
||||||
new RegExp(`E2E ${browserName}`, 'i'),
|
|
||||||
);
|
|
||||||
await cells.nth(4).getByLabel('Member options').click();
|
|
||||||
await page.getByText('Update the role').click();
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
page.getByText('You are the last owner, you cannot change your role.'),
|
|
||||||
).toBeVisible();
|
|
||||||
|
|
||||||
const radioGroup = page.getByLabel('Radio buttons to update the roles');
|
|
||||||
|
|
||||||
const radios = await radioGroup.getByRole('radio').all();
|
|
||||||
for (const radio of radios) {
|
|
||||||
await expect(radio).toBeDisabled();
|
|
||||||
}
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
page.getByRole('button', {
|
|
||||||
name: 'Validate',
|
|
||||||
}),
|
|
||||||
).toBeDisabled();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user