Skip to content

Commit

Permalink
✨(frontend) add crisp chatbot
Browse files Browse the repository at this point in the history
Integrate Crisp chatbot for immediate user support access.

This enables real-time interaction, enhancing user experience
by providing quick assistance.
  • Loading branch information
AntoLC committed Nov 25, 2024
1 parent 34eb4ae commit 2a17330
Show file tree
Hide file tree
Showing 10 changed files with 120 additions and 5 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ and this project adheres to
- ✨(backend) config endpoint #425
- ✨(frontend) config endpoint #424
- ✨(frontend) add sentry #424
- ✨(frontend) add crisp chatbot #273

## Changed

Expand Down
25 changes: 25 additions & 0 deletions src/frontend/apps/e2e/__tests__/app-impress/config.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { expect, test } from '@playwright/test';
import { createDoc } from './common';

const config = {
CRISP_WEBSITE_ID: null,
COLLABORATION_SERVER_URL: 'ws://localhost:4444',
ENVIRONMENT: 'development',
FRONTEND_THEME: 'dsfr',
Expand Down Expand Up @@ -132,4 +133,28 @@ test.describe('Config', () => {
const webSocket = await webSocketPromise;
expect(webSocket.url()).toContain('ws://localhost:4444/');
});

test('it checks that Crisp is trying to init from config endpoint', async ({
page,
}) => {
await page.route('**/api/v1.0/config/', async (route) => {
const request = route.request();
if (request.method().includes('GET')) {
await route.fulfill({
json: {
...config,
CRISP_WEBSITE_ID: '1234',
},
});
} else {
await route.continue();
}
});

await page.goto('/');

await expect(
page.locator('#crisp-chatbox').getByText('Invalid website'),
).toBeVisible();
});
});
1 change: 1 addition & 0 deletions src/frontend/apps/impress/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"@openfun/cunningham-react": "2.9.4",
"@sentry/nextjs": "8.40.0",
"@tanstack/react-query": "5.61.3",
"crisp-sdk-web": "1.0.25",
"i18next": "24.0.0",
"i18next-browser-languagedetector": "8.0.0",
"idb": "8.0.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { Crisp } from 'crisp-sdk-web';
import fetchMock from 'fetch-mock';

import { useAuthStore } from '../useAuthStore';

jest.mock('crisp-sdk-web', () => ({
...jest.requireActual('crisp-sdk-web'),
Crisp: {
isCrispInjected: jest.fn().mockReturnValue(true),
setTokenId: jest.fn(),
user: {
setEmail: jest.fn(),
},
session: {
reset: jest.fn(),
},
},
}));

describe('useAuthStore', () => {
afterEach(() => {
jest.clearAllMocks();
fetchMock.restore();
});

it('checks support session is terminated when logout', () => {
window.$crisp = true;
Object.defineProperty(window, 'location', {
value: {
...window.location,
replace: jest.fn(),
},
writable: true,
});

useAuthStore.getState().logout();

expect(Crisp.session.reset).toHaveBeenCalled();
});
});
2 changes: 2 additions & 0 deletions src/frontend/apps/impress/src/core/auth/useAuthStore.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { create } from 'zustand';

import { baseApiUrl } from '@/api';
import { terminateCrispSession } from '@/services';

import { User, getMe } from './api';
import { PATH_AUTH_LOCAL_STORAGE } from './conf';
Expand Down Expand Up @@ -42,6 +43,7 @@ export const useAuthStore = create<AuthStore>((set, get) => ({
window.location.replace(`${baseApiUrl()}authenticate/`);
},
logout: () => {
terminateCrispSession();
window.location.replace(`${baseApiUrl()}logout/`);
},
// If we try to access a specific page and we are not authenticated
Expand Down
9 changes: 9 additions & 0 deletions src/frontend/apps/impress/src/core/config/ConfigProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { PropsWithChildren, useEffect } from 'react';

import { Box } from '@/components';
import { useCunninghamTheme } from '@/cunningham';
import { configureCrispSession } from '@/services';
import { useSentryStore } from '@/stores/useSentryStore';

import { useConfig } from './api/useConfig';
Expand All @@ -28,6 +29,14 @@ export const ConfigProvider = ({ children }: PropsWithChildren) => {
setTheme(conf.FRONTEND_THEME);
}, [conf?.FRONTEND_THEME, setTheme]);

useEffect(() => {
if (!conf?.CRISP_WEBSITE_ID) {
return;
}

configureCrispSession(conf.CRISP_WEBSITE_ID);
}, [conf?.CRISP_WEBSITE_ID]);

if (!conf) {
return (
<Box $height="100vh" $width="100vw" $align="center" $justify="center">
Expand Down
11 changes: 6 additions & 5 deletions src/frontend/apps/impress/src/core/config/api/useConfig.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ import { APIError, errorCauses, fetchAPI } from '@/api';
import { Theme } from '@/cunningham/';

interface ConfigResponse {
SENTRY_DSN: string;
COLLABORATION_SERVER_URL: string;
ENVIRONMENT: string;
FRONTEND_THEME: Theme;
LANGUAGES: [string, string][];
LANGUAGE_CODE: string;
MEDIA_BASE_URL: string;
ENVIRONMENT: string;
COLLABORATION_SERVER_URL?: string;
CRISP_WEBSITE_ID?: string;
FRONTEND_THEME?: Theme;
MEDIA_BASE_URL?: string;
SENTRY_DSN?: string;
}

export const getConfig = async (): Promise<ConfigResponse> => {
Expand Down
30 changes: 30 additions & 0 deletions src/frontend/apps/impress/src/services/Crisp.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* Configure Crisp chat for real-time support across all pages.
*/

import { Crisp } from 'crisp-sdk-web';

import { User } from '@/core';

export const initializeCrispSession = (user: User) => {
if (!Crisp.isCrispInjected()) {
return;
}
Crisp.setTokenId(`impress-${user.id}`);
Crisp.user.setEmail(user.email);
};

export const configureCrispSession = (websiteId: string) => {
if (Crisp.isCrispInjected()) {
return;
}
Crisp.configure(websiteId);
};

export const terminateCrispSession = () => {
if (!Crisp.isCrispInjected()) {
return;
}
Crisp.setTokenId();
Crisp.session.reset();
};
1 change: 1 addition & 0 deletions src/frontend/apps/impress/src/services/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './Crisp';
5 changes: 5 additions & 0 deletions src/frontend/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5930,6 +5930,11 @@ crelt@^1.0.0:
resolved "https://registry.yarnpkg.com/crelt/-/crelt-1.0.6.tgz#7cc898ea74e190fb6ef9dae57f8f81cf7302df72"
integrity sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==

crisp-sdk-web@1.0.25:
version "1.0.25"
resolved "https://registry.yarnpkg.com/crisp-sdk-web/-/crisp-sdk-web-1.0.25.tgz#5566227dfcc018435b228db2f998d66581e5fdef"
integrity sha512-CWTHFFeHRV0oqiXoPh/aIAKhFs6xcIM4NenGPnClAMCZUDQgQsF1OWmZWmnVNjJriXUmWRgDfeUxcxygS0dCRA==

cross-env@*, cross-env@7.0.3:
version "7.0.3"
resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.3.tgz#865264b29677dc015ba8418918965dd232fc54cf"
Expand Down

0 comments on commit 2a17330

Please sign in to comment.