Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 72 additions & 3 deletions cypress/e2e/account/settings/preferences.cy.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { EmailFrequency } from '@graasp/sdk';
import { EmailFrequency, HttpMethod } from '@graasp/sdk';

import { LANGS } from '../../../../src/config/langs';
import { ACCOUNT_SETTINGS_PATH } from '../../../../src/config/paths';
Expand All @@ -10,10 +10,14 @@ import {
PREFERENCES_EMAIL_FREQUENCY_ID,
PREFERENCES_LANGUAGE_DISPLAY_ID,
PREFERENCES_LANGUAGE_SWITCH_ID,
PREFERENCES_MARKETING_SUBSCRIPTION_DISPLAY_ID,
PREFERENCES_SAVE_BUTTON_ID,
} from '../../../../src/config/selectors';
import { CURRENT_MEMBER, MEMBERS } from '../../../fixtures/members';

const MARKETING_EMAIL_SUBSCRIPTION_SWITCH_SELECTOR =
'[name="I want to receive Graasp\'s updates and communication"]';

describe('Display preferences', () => {
describe('Language', () => {
for (const [lang, expectedLabel] of Object.entries(LANGS)) {
Expand All @@ -37,7 +41,7 @@ describe('Display preferences', () => {
}
});

describe('Email frequency', () => {
describe('Notification frequency', () => {
for (const { emailFreq, expectedText } of [
{
emailFreq: EmailFrequency.Always,
Expand All @@ -58,7 +62,6 @@ describe('Display preferences', () => {
currentMember,
});
cy.visit(ACCOUNT_SETTINGS_PATH);
cy.wait('@getCurrentMember');
cy.get(`#${PREFERENCES_EMAIL_FREQUENCY_ID}`).should(
'contain',
expectedText,
Expand All @@ -67,6 +70,72 @@ describe('Display preferences', () => {
}
});

describe('Marketing emails subscription', () => {
it('Enabled', () => {
cy.setUpApi({
currentSettings: {
marketingEmailsSubscribedAt: new Date().toISOString(),
},
});
cy.intercept(
{
method: HttpMethod.Post,
pathname: /\/api\/members\/current\/marketing\/unsubscribe$/,
},
({ reply }) => {
reply();
},
).as('unsubscribe');

cy.visit(ACCOUNT_SETTINGS_PATH);
cy.get(`#${PREFERENCES_MARKETING_SUBSCRIPTION_DISPLAY_ID}`).should(
'contain',
'Enabled',
);

// edit setting
cy.get(`#${PREFERENCES_EDIT_BUTTON_ID}`).click();
cy.get(MARKETING_EMAIL_SUBSCRIPTION_SWITCH_SELECTOR)
.should('be.checked')
.click();

cy.get(`#${PREFERENCES_SAVE_BUTTON_ID}`).click();
cy.wait('@unsubscribe');
});

it('Disabled', () => {
cy.setUpApi({
currentSettings: {
marketingEmailsSubscribedAt: null,
},
});
cy.intercept(
{
method: HttpMethod.Post,
pathname: /\/api\/members\/current\/marketing\/subscribe$/,
},
({ reply }) => {
reply();
},
).as('subscribe');

cy.visit(ACCOUNT_SETTINGS_PATH);
cy.get(`#${PREFERENCES_MARKETING_SUBSCRIPTION_DISPLAY_ID}`).should(
'contain',
'Disabled',
);

// edit setting
cy.get(`#${PREFERENCES_EDIT_BUTTON_ID}`).click();
cy.get(MARKETING_EMAIL_SUBSCRIPTION_SWITCH_SELECTOR)
.should('not.be.checked')
.click();

cy.get(`#${PREFERENCES_SAVE_BUTTON_ID}`).click();
cy.wait('@subscribe');
});
});

describe('Enable Analytics', () => {
for (const { enableSaveActions, expectedLabel } of [
{ enableSaveActions: true, expectedLabel: 'Enabled' },
Expand Down
10 changes: 10 additions & 0 deletions cypress/support/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ import {
mockGetChildren,
mockGetCurrentMember,
mockGetCurrentMemberAvatar,
mockGetCurrentSettings,
mockGetDescendants,
mockGetItem,
mockGetItemBookmarks,
Expand Down Expand Up @@ -278,6 +279,7 @@ Cypress.Commands.add(
'setUpApi',
({
currentMember = CURRENT_MEMBER,
currentSettings = {},
currentGuest = null,
currentProfile = MEMBER_PUBLIC_PROFILE,
storageAmountInBytes = 10000,
Expand Down Expand Up @@ -537,6 +539,14 @@ Cypress.Commands.add(
mockRejectMembershipRequest();

mockEnroll();

const completeCurrentSettings = {
marketingEmailsSubscribedAt: new Date().toISOString(),
notificationFrequency: currentMember.extra.emailFreq,
enableSaveActions: currentMember.enableSaveActions,
...currentSettings,
};
mockGetCurrentSettings(completeCurrentSettings);
},
);

Expand Down
21 changes: 17 additions & 4 deletions cypress/support/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import { CyHttpMessages } from 'cypress/types/net-stubbing';
import { StatusCodes } from 'http-status-codes';
import { v4 } from 'uuid';

import { Profile } from '@/openapi/client/types.gen';
import { CurrentSettings, Profile } from '@/openapi/client/types.gen';

import { ITEM_PAGE_SIZE, SETTINGS } from '../../src/modules/builder/constants';
import { API_ROUTES } from '../../src/query/routes';
Expand Down Expand Up @@ -73,7 +73,6 @@ const {
buildDownloadFilesRoute,
buildEditItemRoute,
buildExportItemChatRoute,
buildGetCurrentMemberRoute,
buildGetItemChatRoute,
buildGetItemGeolocationRoute,
buildGetItemInvitationsForItemRoute,
Expand Down Expand Up @@ -197,14 +196,14 @@ export const mockGetCurrentMember = (
cy.intercept(
{
method: HttpMethod.Get,
pathname: `/${buildGetCurrentMemberRoute()}`,
pathname: /\/members\/current$/,
},
handler,
).as('getCurrentMember');
cy.intercept(
{
method: HttpMethod.Get,
pathname: `/api/${buildGetCurrentMemberRoute()}`,
pathname: /\/api\/members\/current$/,
},
handler,
).as('getCurrentMemberAPI');
Expand Down Expand Up @@ -2398,3 +2397,17 @@ export const mockGetItemMembershipsForItem = (
},
).as('getItemMemberships');
};

export const mockGetCurrentSettings = (
currentSettings: CurrentSettings,
): void => {
cy.intercept(
{
method: HttpMethod.Get,
pathname: /\/api\/members\/current\/settings$/,
},
({ reply }) => {
reply(currentSettings);
},
).as('getCurrentSettings');
};
9 changes: 7 additions & 2 deletions cypress/support/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {
import type {
ChatMention,
ChatMessage,
CompleteGuest,
Expand All @@ -23,7 +23,11 @@ import {
ThumbnailsBySize,
} from '@graasp/sdk';

import { ItemVisibility, Profile } from '@/openapi/client/types.gen';
import type {
CurrentSettings,
ItemVisibility,
Profile,
} from '@/openapi/client/types.gen';

export type ItemForTest = DiscriminatedItem & {
geolocation?: Partial<ItemGeolocation>;
Expand All @@ -48,6 +52,7 @@ export type FileItemForTest = FileItemType & {
};
export type ApiConfig = {
currentGuest?: CompleteGuest | null;
currentSettings?: Partial<CurrentSettings>;
hasPassword?: boolean;
currentProfile?: Profile | null;
getCurrentProfileError?: boolean;
Expand Down
2 changes: 2 additions & 0 deletions src/config/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ export const SETTINGS_PAGE_CONTAINER_ID = 'settings-page-container';

export const PREFERENCES_LANGUAGE_SWITCH_ID = 'preferences-language-switch';
export const PREFERENCES_LANGUAGE_DISPLAY_ID = 'preferences-language-display';
export const PREFERENCES_MARKETING_SUBSCRIPTION_DISPLAY_ID =
'preferences-marketing-subscription-display';
export const PREFERENCES_ANALYTICS_SWITCH_ID = 'preferences-analytics-switch';
export const PREFERENCES_EMAIL_FREQUENCY_ID = 'preferences-email-frequency';
export const PREFERENCES_EDIT_BUTTON_ID = 'preferences-edit-button';
Expand Down
7 changes: 6 additions & 1 deletion src/locales/en/account.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,12 @@
"PROFILE_LANGUAGE_TITLE": "Language",
"PROFILE_CREATED_AT_INFO": "Member since {{date}}",
"PROFILE_EMAIL_TITLE": "Email",
"PROFILE_EMAIL_FREQUENCY_TITLE": "Email Frequency",
"PROFILE_EMAIL_FREQUENCY_TITLE": "Notification Frequency",
"PROFILE_ENABLE_EMAIL_SUBSCRIPTION": {
"TITLE": "I want to receive Graasp's updates and communication",
"ENABLED": "Enabled",
"DISABLED": "Disabled"
},
"PROFILE_SAVE_ACTIONS_TITLE": "Enable Analytics",
"PROFILE_SAVE_ACTIONS_ENABLED": "Enabled",
"PROFILE_SAVE_ACTIONS_DISABLED": "Disabled",
Expand Down
7 changes: 6 additions & 1 deletion src/locales/fr/account.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,12 @@
"PROFILE_LANGUAGE_TITLE": "Langue",
"PROFILE_CREATED_AT_INFO": "Membre depuis {{date}}",
"PROFILE_EMAIL_TITLE": "Email",
"PROFILE_EMAIL_FREQUENCY_TITLE": "Fréquence d'envoi d'email",
"PROFILE_EMAIL_FREQUENCY_TITLE": "Fréquence d'envoi des notifications",
"PROFILE_ENABLE_EMAIL_SUBSCRIPTION": {
"TITLE": "Je veux recevoir les informations sur les mises à jour et communication de Graasp",
"ENABLED": "Activé",
"DISABLED": "Désactivé"
},
"PROFILE_SAVE_ACTIONS_TITLE": "Activer la sauvegarde des interactions",
"PROFILE_SAVE_ACTIONS_ENABLED": "Activé",
"PROFILE_SAVE_ACTIONS_DISABLED": "Désactivé",
Expand Down
2 changes: 1 addition & 1 deletion src/modules/account/settings/SettingItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Stack, Typography } from '@mui/material';
type Props = {
title: string;
content?: ReactNode;
contentId: string;
contentId?: string;
};
export function SettingItem({
title,
Expand Down
72 changes: 72 additions & 0 deletions src/modules/account/settings/pages/SettingsPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { type JSX, Suspense } from 'react';
import { useTranslation } from 'react-i18next';

import { Skeleton, Stack } from '@mui/material';

import { useSuspenseQuery } from '@tanstack/react-query';

import { ScreenLayout } from '@/components/layout/ScreenLayout';
import { DEFAULT_LANG, NS } from '@/config/constants';
import { SETTINGS_PAGE_CONTAINER_ID } from '@/config/selectors';
import { MemberCard } from '@/modules/home/MemberCard';
import { getCurrentSettingsOptions } from '@/openapi/client/@tanstack/react-query.gen';

import { DeleteMemberSection } from '~account/settings/DeleteMemberSection';
import { ExportMemberData } from '~account/settings/ExportMemberData';
import { Password } from '~account/settings/password/Password';
import { Preferences } from '~account/settings/preferences/Preferences';
import { PersonalInformation } from '~account/settings/profile/PersonalInformation';
import { PublicProfile } from '~account/settings/publicProfile/PublicProfile';

export function SettingsPage(): JSX.Element {
return (
<Suspense fallback={<SettingsLoader />}>
<Settings />
</Suspense>
);
}

function Settings(): JSX.Element {
const { t } = useTranslation(NS.Account);
const { data: memberSettings } = useSuspenseQuery(
getCurrentSettingsOptions(),
);

const {
lang,
enableSaveActions,
notificationFrequency,
marketingEmailsSubscribedAt,
} = memberSettings;

return (
<ScreenLayout
id={SETTINGS_PAGE_CONTAINER_ID}
title={t('MAIN_MENU.SETTINGS')}
>
<MemberCard />
<PersonalInformation />
<Password />
<PublicProfile />
<Preferences
lang={lang ?? DEFAULT_LANG}
enableSaveActions={enableSaveActions}
notificationFrequency={notificationFrequency}
marketingEmailsSubscribedAt={marketingEmailsSubscribedAt}
/>
<ExportMemberData />
<DeleteMemberSection />
</ScreenLayout>
);
}

function SettingsLoader() {
return (
<Stack gap={3}>
<Skeleton width="100%" height={50} sx={{ transform: 'unset' }}></Skeleton>
<Skeleton height={120} sx={{ transform: 'unset' }}></Skeleton>
<Skeleton height={200} sx={{ transform: 'unset' }}></Skeleton>
<Skeleton height={200} sx={{ transform: 'unset' }}></Skeleton>
</Stack>
);
}
Loading
Loading