Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨(frontend) add crisp chatbot #273

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 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
5 changes: 4 additions & 1 deletion .github/workflows/docker-hub.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ on:

env:
DOCKER_USER: 1001:127
CRISP_WEBSITE_ID: ""

jobs:
build-and-push-backend:
Expand Down Expand Up @@ -99,7 +100,9 @@ jobs:
context: .
file: ./src/frontend/Dockerfile
target: frontend-production
build-args: DOCKER_USER=${{ env.DOCKER_USER }}:-1000
build-args: |
DOCKER_USER=${{ env.DOCKER_USER }}:-1000
CRISP_WEBSITE_ID=${{ env.CRISP_WEBSITE_ID }}
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
Expand Down
2 changes: 1 addition & 1 deletion secrets
3 changes: 3 additions & 0 deletions src/frontend/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ ENV NEXT_PUBLIC_Y_PROVIDER_URL=${Y_PROVIDER_URL}
ARG API_ORIGIN
ENV NEXT_PUBLIC_API_ORIGIN=${API_ORIGIN}

ARG CRISP_WEBSITE_ID
ENV NEXT_PUBLIC_CRISP_WEBSITE_ID=${CRISP_WEBSITE_ID}

RUN yarn build

# ---- Front-end image ----
Expand Down
3 changes: 2 additions & 1 deletion src/frontend/apps/impress/.env
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
NEXT_PUBLIC_API_ORIGIN=
NEXT_PUBLIC_Y_PROVIDER_URL=
NEXT_PUBLIC_MEDIA_URL=
NEXT_PUBLIC_THEME=dsfr
NEXT_PUBLIC_THEME=dsfr
NEXT_PUBLIC_CRISP_WEBSITE_ID=
1 change: 1 addition & 0 deletions src/frontend/apps/impress/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"@hocuspocus/provider": "2.13.5",
"@openfun/cunningham-react": "2.9.4",
"@tanstack/react-query": "5.56.2",
"crisp-sdk-web": "1.0.25",
"i18next": "23.15.1",
"idb": "8.0.0",
"lodash": "4.17.21",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { waitFor } from '@testing-library/react';
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: {
setTokenId: jest.fn(),
user: {
setEmail: jest.fn(),
},
session: {
reset: jest.fn(),
},
},
}));

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

it('checks initialize support session when initAuth', async () => {
window.$crisp = true;
fetchMock.mock('end:users/me/', {
id: '123456',
email: 'test@email.com',
});

useAuthStore.getState().initAuth();

await waitFor(() => {
expect(Crisp.setTokenId).toHaveBeenCalledWith('123456');
});

expect(Crisp.user.setEmail).toHaveBeenCalledWith('test@email.com');
});

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();
});
});
6 changes: 6 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,10 @@
import { create } from 'zustand';

import { baseApiUrl } from '@/core/conf';
import {
initializeSupportSession,
terminateSupportSession,
} from '@/hook/useSupport';

import { User, getMe } from './api';
import { PATH_AUTH_LOCAL_STORAGE } from './conf';
Expand Down Expand Up @@ -36,6 +40,7 @@ export const useAuthStore = create<AuthStore>((set) => ({
return;
}

initializeSupportSession(data);
set({ authenticated: true, userData: data });
})
.catch(() => {})
Expand All @@ -53,6 +58,7 @@ export const useAuthStore = create<AuthStore>((set) => ({
window.location.replace(`${baseApiUrl()}authenticate/`);
},
logout: () => {
terminateSupportSession();
window.location.replace(`${baseApiUrl()}logout/`);
},
}));
1 change: 1 addition & 0 deletions src/frontend/apps/impress/src/custom-next.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,6 @@ namespace NodeJS {
NEXT_PUBLIC_Y_PROVIDER_URL?: string;
NEXT_PUBLIC_SW_DEACTIVATED?: string;
NEXT_PUBLIC_THEME?: string;
NEXT_PUBLIC_CRISP_WEBSITE_ID?: string;
}
}
30 changes: 30 additions & 0 deletions src/frontend/apps/impress/src/hook/__tests__/useSupport.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { renderHook } from '@testing-library/react';
import { Crisp } from 'crisp-sdk-web';

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

jest.mock('crisp-sdk-web', () => ({
...jest.requireActual('crisp-sdk-web'),
Crisp: {
configure: jest.fn(),
},
}));

describe('useSupport', () => {
afterEach(() => jest.clearAllMocks());

it('checks that env NEXT_PUBLIC_CRISP_WEBSITE_ID not set give a warning', () => {
process.env.NEXT_PUBLIC_CRISP_WEBSITE_ID = '';
jest.spyOn(console, 'warn').mockImplementation(() => {});

renderHook(() => useSupport());
expect(console.warn).toHaveBeenCalledWith('Crisp Website ID is not set');
});

it('checks Crisp is configured', () => {
process.env.NEXT_PUBLIC_CRISP_WEBSITE_ID = '123456';
renderHook(() => useSupport());

expect(Crisp.configure).toHaveBeenCalledWith('123456');
});
});
43 changes: 43 additions & 0 deletions src/frontend/apps/impress/src/hook/useSupport.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Crisp } from 'crisp-sdk-web';
import { useEffect } from 'react';

import { User } from '@/core';

const isCrispConfigured = (): boolean => {
return typeof window !== 'undefined' && !!window.$crisp;
};

export const initializeSupportSession = (user: User) => {
if (!isCrispConfigured()) {
return;
}
Crisp.setTokenId(user.id);
Copy link
Collaborator

@lebaudantoine lebaudantoine Sep 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure anymore of this part … let's discuss it before merging, I am reading their doc

Crisp.user.setEmail(user.email);
};

export const terminateSupportSession = () => {
if (!isCrispConfigured()) {
return;
}
Crisp.session.reset();
};

/**
* Configure Crisp chat for real-time support across all pages.
*/
export const useSupport = () => {
useEffect(() => {
const CRISP_WEBSITE_ID = process.env.NEXT_PUBLIC_CRISP_WEBSITE_ID;

if (!CRISP_WEBSITE_ID) {
console.warn('Crisp Website ID is not set');
return;
}
if (isCrispConfigured()) {
return;
}
Crisp.configure(CRISP_WEBSITE_ID);
}, []);

return null;
};
4 changes: 4 additions & 0 deletions src/frontend/apps/impress/src/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next';

import { AppProvider } from '@/core/';
import { useSWRegister } from '@/features/service-worker/';
import { useSupport } from '@/hook/useSupport';
import { NextPageWithLayout } from '@/types/next';

import './globals.css';
Expand All @@ -17,6 +18,9 @@ export default function App({ Component, pageProps }: AppPropsWithLayout) {
const getLayout = Component.getLayout ?? ((page) => page);
const { t } = useTranslation();

// Initialize Crisp, the support chat used among all La Suite products
useSupport();

return (
<>
<Head>
Expand Down
5 changes: 5 additions & 0 deletions src/frontend/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5105,6 +5105,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
Loading