From 9ec7eddaed66630d4325f3910c03137680f71212 Mon Sep 17 00:00:00 2001 From: Lebaud Antoine Date: Mon, 25 Mar 2024 23:06:18 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8(frontend)=20add=20logout=20button?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rework the header based on latest Johann's design, which introduced a dropdown menu to mange user account. In this menu, you can find a logout button, which ends up the backend session by calling the logout endpoint. Please that automatic redirection when receiving the backend response were disabled. We handle it in our custom hook, which reload the page. Has the session cookie have been cleared, on reloading the page, a new loggin flow is initiated, and the user is redirected to the OIDC provider. Please note, the homepage design/organization is still under discussion, I prefered to ship a first increment. The logout feature will be quite useful in staging to play and test our UI. --- .../desk/src/api/__tests__/fetchApi.test.tsx | 23 +++++------- src/frontend/apps/desk/src/api/fetchApi.ts | 6 +-- .../apps/desk/src/components/DropButton.tsx | 4 ++ src/frontend/apps/desk/src/core/auth/Auth.tsx | 2 +- .../apps/desk/src/core/auth/api/index.ts | 3 +- .../apps/desk/src/core/auth/api/logout.ts | 8 ++++ .../apps/desk/src/core/auth/useAuthStore.tsx | 7 ++-- .../src/features/header/AccountDropdown.tsx | 34 +++++++++++++++++ .../apps/desk/src/features/header/Header.tsx | 35 +++--------------- .../header/assets/icon-my-account.png | Bin 7957 -> 0 bytes .../apps/desk/src/i18n/translations.json | 2 + .../e2e/__tests__/app-desk/header.spec.ts | 18 +++++++++ 12 files changed, 89 insertions(+), 53 deletions(-) create mode 100644 src/frontend/apps/desk/src/core/auth/api/logout.ts create mode 100644 src/frontend/apps/desk/src/features/header/AccountDropdown.tsx delete mode 100644 src/frontend/apps/desk/src/features/header/assets/icon-my-account.png diff --git a/src/frontend/apps/desk/src/api/__tests__/fetchApi.test.tsx b/src/frontend/apps/desk/src/api/__tests__/fetchApi.test.tsx index d08d86d..cd20fb8 100644 --- a/src/frontend/apps/desk/src/api/__tests__/fetchApi.test.tsx +++ b/src/frontend/apps/desk/src/api/__tests__/fetchApi.test.tsx @@ -34,25 +34,22 @@ describe('fetchAPI', () => { }); it('logout if 401 response', async () => { - const mockReplace = jest.fn(); - Object.defineProperty(window, 'location', { - configurable: true, - enumerable: true, - value: { - replace: mockReplace, - }, + useAuthStore.setState({ + authenticated: true, + userData: { id: '123', email: 'test@test.com' }, }); - useAuthStore.setState({ userData: { email: 'test@test.com', id: '1234' } }); - fetchMock.mock('http://some.api.url/api/v1.0/some/url', 401); + fetchMock.mock('http://some.api.url/api/v1.0/logout/', 302); await fetchAPI('some/url'); - expect(useAuthStore.getState().userData).toBeUndefined(); + await Promise.all([fetchMock.flush()]); - expect(mockReplace).toHaveBeenCalledWith( - 'http://some.api.url/api/v1.0/authenticate/', - ); + expect(fetchMock.lastUrl()).toEqual('http://some.api.url/api/v1.0/logout/'); + + const { userData, authenticated } = useAuthStore.getState(); + expect(userData).toBeUndefined(); + expect(authenticated).toBeFalsy(); }); }); diff --git a/src/frontend/apps/desk/src/api/fetchApi.ts b/src/frontend/apps/desk/src/api/fetchApi.ts index c2e2e2b..48b381b 100644 --- a/src/frontend/apps/desk/src/api/fetchApi.ts +++ b/src/frontend/apps/desk/src/api/fetchApi.ts @@ -1,4 +1,4 @@ -import { login, useAuthStore } from '@/core/auth'; +import { useAuthStore } from '@/core/auth'; /** * Retrieves the CSRF token from the document's cookies. @@ -29,12 +29,8 @@ export const fetchAPI = async (input: string, init?: RequestInit) => { }, }); - // todo - handle 401, redirect to login screen - // todo - please have a look to this documentation page https://mozilla-django-oidc.readthedocs.io/en/stable/xhr.html if (response.status === 401) { logout(); - // Fix - force re-logging the user, will be refactored - login(); } return response; diff --git a/src/frontend/apps/desk/src/components/DropButton.tsx b/src/frontend/apps/desk/src/components/DropButton.tsx index 2493c9c..f79b78e 100644 --- a/src/frontend/apps/desk/src/components/DropButton.tsx +++ b/src/frontend/apps/desk/src/components/DropButton.tsx @@ -23,6 +23,10 @@ const StyledButton = styled(Button)` background: none; outline: none; transition: all 0.2s ease-in-out; + font-family: Marianne, Arial, serif; + font-weight: 500; + font-size: 0.938rem; + text-wrap: nowrap; `; interface DropButtonProps { diff --git a/src/frontend/apps/desk/src/core/auth/Auth.tsx b/src/frontend/apps/desk/src/core/auth/Auth.tsx index 865a815..bc314a7 100644 --- a/src/frontend/apps/desk/src/core/auth/Auth.tsx +++ b/src/frontend/apps/desk/src/core/auth/Auth.tsx @@ -10,7 +10,7 @@ export const Auth = ({ children }: PropsWithChildren) => { useEffect(() => { initAuth(); - }, [initAuth]); + }, [initAuth, authenticated]); if (!authenticated) { return ( diff --git a/src/frontend/apps/desk/src/core/auth/api/index.ts b/src/frontend/apps/desk/src/core/auth/api/index.ts index d81520d..45350f2 100644 --- a/src/frontend/apps/desk/src/core/auth/api/index.ts +++ b/src/frontend/apps/desk/src/core/auth/api/index.ts @@ -1,2 +1,3 @@ -export * from './getMe'; export * from './types'; +export * from './getMe'; +export * from './logout'; diff --git a/src/frontend/apps/desk/src/core/auth/api/logout.ts b/src/frontend/apps/desk/src/core/auth/api/logout.ts new file mode 100644 index 0000000..31bbea7 --- /dev/null +++ b/src/frontend/apps/desk/src/core/auth/api/logout.ts @@ -0,0 +1,8 @@ +import { fetchAPI } from '@/api'; + +export const logout = async () => { + await fetchAPI(`logout/`, { + method: 'POST', + redirect: 'manual', + }); +}; diff --git a/src/frontend/apps/desk/src/core/auth/useAuthStore.tsx b/src/frontend/apps/desk/src/core/auth/useAuthStore.tsx index e2a2f3a..6b430e1 100644 --- a/src/frontend/apps/desk/src/core/auth/useAuthStore.tsx +++ b/src/frontend/apps/desk/src/core/auth/useAuthStore.tsx @@ -1,6 +1,6 @@ import { create } from 'zustand'; -import { User, getMe } from './api'; +import { User, getMe, logout } from './api'; export const login = () => { window.location.replace( @@ -30,11 +30,12 @@ export const useAuthStore = create((set) => ({ set({ authenticated: true, userData: data }); }) .catch(() => { - // todo - implement a proper login screen to prevent automatic navigation. login(); }); }, logout: () => { - set(initialState); + void logout().then(() => { + set(initialState); + }); }, })); diff --git a/src/frontend/apps/desk/src/features/header/AccountDropdown.tsx b/src/frontend/apps/desk/src/features/header/AccountDropdown.tsx new file mode 100644 index 0000000..13c77b5 --- /dev/null +++ b/src/frontend/apps/desk/src/features/header/AccountDropdown.tsx @@ -0,0 +1,34 @@ +import { Button } from '@openfun/cunningham-react'; +import React from 'react'; +import { useTranslation } from 'react-i18next'; + +import { Box, DropButton, Text } from '@/components'; +import { useAuthStore } from '@/core/auth'; + +export const AccountDropdown = () => { + const { t } = useTranslation(); + const { logout } = useAuthStore(); + + return ( + + {t('My account')} + + arrow_drop_down + + + } + > + + + ); +}; diff --git a/src/frontend/apps/desk/src/features/header/Header.tsx b/src/frontend/apps/desk/src/features/header/Header.tsx index cf648ac..5f13419 100644 --- a/src/frontend/apps/desk/src/features/header/Header.tsx +++ b/src/frontend/apps/desk/src/features/header/Header.tsx @@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next'; import styled from 'styled-components'; import { Box, Text } from '@/components/'; +import { AccountDropdown } from '@/features/header/AccountDropdown'; import { ApplicationsMenu } from '@/features/header/ApplicationsMenu'; import { LanguagePicker } from '../language/'; @@ -11,7 +12,6 @@ import { LanguagePicker } from '../language/'; import { default as IconApplication } from './assets/icon-application.svg?url'; import { default as IconGouv } from './assets/icon-gouv.svg?url'; import { default as IconMarianne } from './assets/icon-marianne.svg?url'; -import IconMyAccount from './assets/icon-my-account.png'; export const HEADER_HEIGHT = '100px'; @@ -62,35 +62,10 @@ export const Header = () => { - button { - padding: 0; - } - `} - $gap="5rem" - $justify="flex-end" - $direction="row" - > - - - - - - - John Doe - - {t(`Profile - - - + + + + diff --git a/src/frontend/apps/desk/src/features/header/assets/icon-my-account.png b/src/frontend/apps/desk/src/features/header/assets/icon-my-account.png deleted file mode 100644 index 54bd64cc7e3f92769d86ec0fe7a06f0ac8980b59..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7957 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 { await expect(header.getByAltText('Language Icon')).toBeVisible(); await expect(header.getByText('My account')).toBeVisible(); }); + + test('checks logout button', async ({ page }) => { + await page + .getByRole('button', { + name: 'My account', + }) + .click(); + + await page + .getByRole('button', { + name: 'Logout', + }) + .click(); + + // FIXME - assert the session has been killed in Keycloak + + await expect(page.getByRole('button', { name: 'Sign in' })).toBeVisible(); + }); });