diff --git a/README.md b/README.md index b3b16b59..7833c0d1 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,16 @@ # Graasp Auth -[![gitlocalized ](https://gitlocalize.com/repo/9425/whole_project/badge.svg)](https://gitlocalize.com/repo/9425?utm_source=badge) +[gitlocalized](https://gitlocalize.com/repo/9425/whole_project/badge.svg)]() Create an `.env.development` file with: ```sh VITE_PORT=3001 -VITE_API_HOST=http://localhost:3000 +VITE_GRAASP_API_HOST=http://localhost:3000 VITE_VERSION=latest VITE_GRAASP_BUILDER_HOST=http://localhost:3111 VITE_SHOW_NOTIFICATIONS=true -VITE_GRAASP_AUT_HOST=http://localhost:3001 +VITE_GRAASP_AUTH_HOST=http://localhost:3001 VITE_GRAASP_LANDING_PAGE_ORIGIN=https://graasp.org VITE_RECAPTCHA_SITE_KEY= @@ -22,11 +22,11 @@ For running tests locally create a `.env.test` file: ```sh VITE_PORT=3002 -VITE_API_HOST=http://localhost:3636 +VITE_GRAASP_API_HOST=http://localhost:3636 VITE_VERSION=latest VITE_GRAASP_BUILDER_HOST=http://localhost:3111 VITE_SHOW_NOTIFICATIONS=true -VITE_GRAASP_AUT_HOST=http://localhost:3001 +VITE_GRAASP_AUTH_HOST=http://localhost:3001 VITE_RECAPTCHA_SITE_KEY= ``` diff --git a/cypress.config.ts b/cypress.config.ts index 52a3e074..d27d6493 100644 --- a/cypress.config.ts +++ b/cypress.config.ts @@ -13,7 +13,7 @@ export default defineConfig({ const newConfig = { ...config, env: { - API_HOST: process.env.REACT_APP_API_HOST, + VITE_GRAASP_API_HOST: process.env.VITE_GRAASP_API_HOST, }, }; setupEvents(on, newConfig); diff --git a/cypress/e2e/SignIn.cy.ts b/cypress/e2e/SignIn.cy.ts index 4df94ef5..1f635f40 100644 --- a/cypress/e2e/SignIn.cy.ts +++ b/cypress/e2e/SignIn.cy.ts @@ -2,6 +2,9 @@ import { SIGN_IN_PATH } from '../../src/config/paths'; import { MEMBERS } from '../fixtures/members'; describe('Name and Email Validation', () => { + beforeEach(() => { + cy.setUpApi(); + }); it('Sign In', () => { const { GRAASP, WRONG_EMAIL } = MEMBERS; cy.visit(SIGN_IN_PATH); diff --git a/cypress/e2e/SignUp.cy.ts b/cypress/e2e/SignUp.cy.ts index 5f640fc9..cfac3682 100644 --- a/cypress/e2e/SignUp.cy.ts +++ b/cypress/e2e/SignUp.cy.ts @@ -93,7 +93,7 @@ describe('SignUp', () => { }; cy.intercept(API_ROUTES.buildGetInvitationRoute(invitation.id), { statusCode: 404, - body: '404 Not Found!', + body: { message: '404 Not Found!' }, }); const search = new URLSearchParams(); search.set('invitationId', invitation.id); diff --git a/cypress/fixtures/members.ts b/cypress/fixtures/members.ts index d7f1790f..1ac5831e 100644 --- a/cypress/fixtures/members.ts +++ b/cypress/fixtures/members.ts @@ -20,6 +20,7 @@ export const MEMBERS: { createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), extra: {}, + enableSaveActions: true, }, GRAASP_OTHER: { id: 'graasp_other-id', @@ -33,6 +34,7 @@ export const MEMBERS: { createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), extra: {}, + enableSaveActions: true, }, WRONG_NAME: { id: 'id1', @@ -45,6 +47,7 @@ export const MEMBERS: { createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), extra: {}, + enableSaveActions: true, }, WRONG_EMAIL: { id: 'id2', @@ -58,6 +61,7 @@ export const MEMBERS: { createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), extra: {}, + enableSaveActions: true, }, WRONG_PASSWORD: { id: 'id3', @@ -71,6 +75,7 @@ export const MEMBERS: { createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), extra: {}, + enableSaveActions: true, }, BOB: { id: 'ecafbd2a-5642-31fb-ae93-0242ac130004', @@ -80,6 +85,7 @@ export const MEMBERS: { extra: { lang: 'en' }, type: MemberType.Individual, updatedAt: new Date().toISOString(), + enableSaveActions: true, }, CEDRIC: { id: 'ecafbd2a-5642-31fb-ae93-0242ac130006', @@ -89,6 +95,7 @@ export const MEMBERS: { type: MemberType.Individual, updatedAt: new Date().toISOString(), extra: {}, + enableSaveActions: true, }, }; diff --git a/cypress/support/server.ts b/cypress/support/server.ts index e53b3642..2a2858e2 100644 --- a/cypress/support/server.ts +++ b/cypress/support/server.ts @@ -7,7 +7,7 @@ const { buildGetMember, GET_CURRENT_MEMBER_ROUTE } = API_ROUTES; // use simple id format for tests export const ID_FORMAT = '(?=.*[0-9])(?=.*[a-zA-Z])([a-z0-9-]+)'; -const API_HOST = Cypress.env('API_HOST'); +const API_HOST = Cypress.env('VITE_GRAASP_API_HOST'); export const redirectionReply = { headers: { 'content-type': 'application/json' }, @@ -89,16 +89,17 @@ export const mockGetMembers = (members) => { ).as('getMembers'); }; -export const mockGetStatus = () => { +export const mockGetStatus = (shouldThrowServerError = false) => { cy.intercept( { method: 'get', url: `${API_HOST}/status`, }, - ({ url, reply }) => { - return reply({ - statusCode: StatusCodes.OK, - }); + ({ reply }) => { + if (shouldThrowServerError) { + return reply({ statusCode: StatusCodes.INTERNAL_SERVER_ERROR }); + } + return reply({ statusCode: StatusCodes.OK }); }, ).as('getStatus'); }; diff --git a/index.html b/index.html index 962a3a9c..dc4e2748 100644 --- a/index.html +++ b/index.html @@ -1,4 +1,4 @@ - + @@ -19,7 +19,7 @@ Graasp Auth diff --git a/package.json b/package.json index a9823a6b..6051c879 100644 --- a/package.json +++ b/package.json @@ -14,9 +14,9 @@ "@emotion/react": "11.11.4", "@emotion/styled": "11.11.0", "@graasp/query-client": "3.0.2", - "@graasp/sdk": "4.2.1", + "@graasp/sdk": "4.7.2", "@graasp/translations": "1.25.3", - "@graasp/ui": "4.11.0", + "@graasp/ui": "4.14.2", "@mui/icons-material": "5.15.14", "@mui/lab": "5.0.0-alpha.169", "@mui/material": "5.15.14", diff --git a/src/components/App.tsx b/src/App.tsx similarity index 71% rename from src/components/App.tsx rename to src/App.tsx index fb3065d4..cc7912da 100644 --- a/src/components/App.tsx +++ b/src/App.tsx @@ -1,17 +1,17 @@ import * as Sentry from '@sentry/react'; import { Route, BrowserRouter as Router, Routes } from 'react-router-dom'; +import ErrorFallback from './components/ErrorFallback'; +import MobileAuth from './components/MobileAuth'; +import Redirection from './components/Redirection'; +import SignIn from './components/SignIn'; +import SignUp from './components/SignUp'; import { HOME_PATH, MOBILE_AUTH_PATH, SIGN_IN_PATH, SIGN_UP_PATH, -} from '../config/paths'; -import ErrorFallback from './ErrorFallback'; -import MobileAuth from './MobileAuth'; -import Redirection from './Redirection'; -import SignIn from './SignIn'; -import SignUp from './SignUp'; +} from './config/paths'; const App = () => ( } showDialog> diff --git a/src/Root.tsx b/src/Root.tsx index 42e480f6..80b59e31 100644 --- a/src/Root.tsx +++ b/src/Root.tsx @@ -7,7 +7,7 @@ import { ThemeProvider } from '@graasp/ui'; import { CssBaseline } from '@mui/material'; -import App from './components/App'; +import App from './App'; import { RECAPTCHA_SITE_KEY, SHOW_NOTIFICATIONS } from './config/env'; import i18nConfig, { useCommonTranslation } from './config/i18n'; import { diff --git a/src/components/EmailInput.tsx b/src/components/EmailInput.tsx index daaf7c7a..9351c762 100644 --- a/src/components/EmailInput.tsx +++ b/src/components/EmailInput.tsx @@ -49,7 +49,7 @@ const EmailInput: FC = ({ required={required} value={value} error={Boolean(error)} - helperText={error} + helperText={t(error)} onChange={handleEmailOnChange} id={id} type="email" diff --git a/src/components/SignIn.tsx b/src/components/SignIn.tsx index 7ebf05b7..49ede74a 100644 --- a/src/components/SignIn.tsx +++ b/src/components/SignIn.tsx @@ -4,6 +4,7 @@ import { Link, useLocation } from 'react-router-dom'; import { RecaptchaAction } from '@graasp/sdk'; import { Button, GraaspLogo } from '@graasp/ui'; +import { LoadingButton } from '@mui/lab'; import { Stack, useTheme } from '@mui/material'; import Box from '@mui/material/Box'; import FormControl from '@mui/material/FormControl'; @@ -31,6 +32,7 @@ import EmailInput from './EmailInput'; import FullscreenContainer from './FullscreenContainer'; import StyledTextField from './StyledTextField'; import SuccessContent from './SuccessContent'; +import ErrorDisplay from './common/ErrorDisplay'; const { SIGN_IN_BUTTON, @@ -58,19 +60,35 @@ const SignIn: FC = () => { // enable validation after first click const [shouldValidate, setShouldValidate] = useState(false); - const { mutateAsync: signIn, isSuccess: signInSuccess } = - mutations.useSignIn(); - const { mutateAsync: mobileSignIn, isSuccess: mobileSignInSuccess } = - mutations.useMobileSignIn(); + const { + mutateAsync: signIn, + isSuccess: signInSuccess, + isLoading: isLoadingSignIn, + error: webSignInError, + } = mutations.useSignIn(); + const { + mutateAsync: mobileSignIn, + isSuccess: mobileSignInSuccess, + isLoading: isLoadingMobileSignIn, + error: mobileSignInError, + } = mutations.useMobileSignIn(); const { mutateAsync: signInWithPassword, isSuccess: signInWithPasswordSuccess, + isLoading: isLoadingPasswordSignIn, + error: webPasswordSignInError, } = mutations.useSignInWithPassword(); const { mutateAsync: mobileSignInWithPassword, isSuccess: mobileSignInWithPasswordSuccess, + isLoading: isLoadingMobilePasswordSignIn, + error: mobilePasswordSignInError, } = mutations.useMobileSignInWithPassword(); + const signInError = webSignInError || mobileSignInError; + const passwordSignInError = + webPasswordSignInError || mobilePasswordSignInError; + const handleSignIn = async () => { const lowercaseEmail = email.toLowerCase(); const checkingEmail = emailValidator(lowercaseEmail); @@ -196,26 +214,38 @@ const SignIn: FC = () => { variant="outlined" value={password} error={Boolean(passwordError)} - helperText={passwordError} + helperText={t(passwordError)} onChange={handleOnChangePassword} id={PASSWORD_SIGN_IN_FIELD_ID} type="password" onKeyDown={handleKeypress} /> - + )} {signInMethod === SIGN_IN_METHODS.EMAIL && ( - + <> + + + {t(SIGN_IN_BUTTON)} + + )} diff --git a/src/components/SignUp.tsx b/src/components/SignUp.tsx index db77ca46..c0acb9cb 100644 --- a/src/components/SignUp.tsx +++ b/src/components/SignUp.tsx @@ -1,15 +1,19 @@ import { ChangeEventHandler, useEffect, useState } from 'react'; -import { Link, useLocation, useSearchParams } from 'react-router-dom'; +import { Link, useSearchParams } from 'react-router-dom'; -import { RecaptchaAction } from '@graasp/sdk'; -import { Button, GraaspLogo, Loader } from '@graasp/ui'; - -import { Stack, useTheme } from '@mui/material'; -import FormControl from '@mui/material/FormControl'; +import { + MAX_USERNAME_LENGTH, + MIN_USERNAME_LENGTH, + RecaptchaAction, +} from '@graasp/sdk'; +import { GraaspLogo } from '@graasp/ui'; + +import { LoadingButton } from '@mui/lab'; +import { FormControl, LinearProgress, Stack, useTheme } from '@mui/material'; import Typography from '@mui/material/Typography'; -import { SIGN_IN_PATH } from '../config/constants'; import { useAuthTranslation } from '../config/i18n'; +import { SIGN_IN_PATH } from '../config/paths'; import { hooks, mutations } from '../config/queryClient'; import { EMAIL_SIGN_UP_FIELD_ID, @@ -28,9 +32,15 @@ import EmailInput from './EmailInput'; import FullscreenContainer from './FullscreenContainer'; import StyledTextField from './StyledTextField'; import SuccessContent from './SuccessContent'; +import ErrorDisplay from './common/ErrorDisplay'; -const { SIGN_IN_LINK_TEXT, SIGN_UP_BUTTON, SIGN_UP_HEADER, NAME_FIELD_LABEL } = - AUTH; +const { + SIGN_IN_LINK_TEXT, + SIGN_UP_HEADER, + NAME_FIELD_LABEL, + SIGN_UP_BUTTON, + INVITATIONS_LOADING_MESSAGE, +} = AUTH; const SignUp = () => { const { t, i18n } = useAuthTranslation(); @@ -49,32 +59,46 @@ const SignUp = () => { const agreementFormHook = useAgreementForm(); const { verifyUserAgreements, userHasAcceptedAllTerms } = agreementFormHook; - const { mutateAsync: signUp, isSuccess: signUpSuccess } = - mutations.useSignUp(); - const { mutateAsync: mobileSignUp, isSuccess: mobileSignUpSuccess } = - mutations.useMobileSignUp(); + const { + mutateAsync: signUp, + isSuccess: signUpSuccess, + isLoading: isLoadingSignUp, + error: webRegisterError, + } = mutations.useSignUp(); + const { + mutateAsync: mobileSignUp, + isSuccess: mobileSignUpSuccess, + isLoading: isLoadingMobileSignUp, + error: mobileRegisterError, + } = mutations.useMobileSignUp(); const [searchParams] = useSearchParams(); - const { search } = useLocation(); const { data: invitation, - isSuccess, - isInitialLoading, + isSuccess: isInvitationSuccess, + isInitialLoading: isLoadingInvitations, } = hooks.useInvitation(searchParams.get('invitationId') || undefined); useEffect(() => { - if (isSuccess && invitation) { + if (isInvitationSuccess && invitation) { setEmail(invitation.email); setName(invitation.name ?? ''); } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [invitation, isSuccess]); + }, [invitation, isInvitationSuccess]); // loading invitation - if (isInitialLoading) { - return ; + if (isLoadingInvitations) { + return ( + + {t(INVITATIONS_LOADING_MESSAGE)} + + + ); } + const registerError = webRegisterError || mobileRegisterError; + const handleNameOnChange: ChangeEventHandler = (e) => { const newName = e.target.value; setName(newName); @@ -120,8 +144,29 @@ const SignUp = () => { setSuccessView(false); }; - const renderForm = () => ( - <> + if ((signUpSuccess || mobileSignUpSuccess) && successView) { + return ( + + ); + } + + return ( + + + + + {t(SIGN_UP_HEADER)} + + { variant="outlined" value={name} error={Boolean(nameError)} - helperText={nameError} + helperText={t(nameError, { + min: MIN_USERNAME_LENGTH, + max: MAX_USERNAME_LENGTH, + })} onChange={handleNameOnChange} id={NAME_SIGN_UP_FIELD_ID} disabled={Boolean(invitation?.name)} @@ -143,46 +191,30 @@ const SignUp = () => { shouldValidate={shouldValidate} /> - + - {t(SIGN_IN_LINK_TEXT)} - - ); - - return ( - - {(signUpSuccess || mobileSignUpSuccess) && successView ? ( - - ) : ( - - - - - {t(SIGN_UP_HEADER)} - - - {renderForm()} - - )} - + + {t(SIGN_IN_LINK_TEXT)} + + ); }; -export default SignUp; +const SignUpScreenWrapper = () => ( + + + +); + +export default SignUpScreenWrapper; diff --git a/src/components/StyledTextField.tsx b/src/components/StyledTextField.tsx index 1f8e6091..58c39965 100644 --- a/src/components/StyledTextField.tsx +++ b/src/components/StyledTextField.tsx @@ -1,7 +1,7 @@ import { styled } from '@mui/material'; import TextField from '@mui/material/TextField'; -import { FORM_INPUT_MIN_WIDTH } from '../config/constants'; +const FORM_INPUT_MIN_WIDTH = 300; const StyledTextField = styled(TextField)(() => ({ minWidth: FORM_INPUT_MIN_WIDTH, diff --git a/src/components/common/ErrorDisplay.tsx b/src/components/common/ErrorDisplay.tsx new file mode 100644 index 00000000..de5825eb --- /dev/null +++ b/src/components/common/ErrorDisplay.tsx @@ -0,0 +1,18 @@ +import { Alert } from '@mui/material'; + +import { useMessagesTranslation } from '../../config/i18n'; +import { getErrorMessage } from '../../config/notifier'; + +const ErrorDisplay = ({ error }: { error: Error }): JSX.Element | false => { + const { t: translateMessages } = useMessagesTranslation(); + + if (error) { + return ( + + {translateMessages(getErrorMessage(error))} + + ); + } + return false; +}; +export default ErrorDisplay; diff --git a/src/config/constants.ts b/src/config/constants.ts deleted file mode 100644 index c3702886..00000000 --- a/src/config/constants.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { buildSignInPath } from '@graasp/sdk'; - -import { AUTHENTICATION_HOST, GRAASP_BUILDER_HOST } from './env'; - -export const APP_NAME = 'Graasp Authentication'; - -export const NAME_MAXIMUM_LENGTH = 300; -export const NAME_MINIMUM_LENGTH = 2; - -export const FORM_INPUT_MIN_WIDTH = 300; - -export const ITEM_HEADER_ICON_HEIGHT = 30; -export const AVATAR_ICON_HEIGHT = 30; - -export const MEMBER_PROFILE_PATH = `${GRAASP_BUILDER_HOST}/profile`; -export const SIGN_IN_PATH = buildSignInPath({ host: AUTHENTICATION_HOST }); diff --git a/src/config/messages.ts b/src/config/messages.ts index f1bf2be5..c096d3b1 100644 --- a/src/config/messages.ts +++ b/src/config/messages.ts @@ -1,7 +1 @@ -export const INVALID_EMAIL_ERROR = 'Please enter a valid email address'; -export const EMPTY_EMAIL_ERROR = 'Please enter an email address'; export const PASSWORD_EMPTY_ERROR = 'Please enter a valid password'; -export const USERNAME_ERROR_MAXIMUM_MESSAGE = - 'Please enter a username under 300 characters'; -export const USERNAME_ERROR_MINIMUM_MESSAGE = - 'Please enter a username with more than 2 characters'; diff --git a/src/config/notifier.ts b/src/config/notifier.ts index e7cf40ef..f7b63ff5 100644 --- a/src/config/notifier.ts +++ b/src/config/notifier.ts @@ -14,12 +14,20 @@ const { signInWithPasswordRoutine, } = routines; -const getErrorMessage = ( +export const getErrorMessage = ( error: Parameters[0]['payload']['error'], ) => { + // eslint-disable-next-line no-console + console.log(error); if (error instanceof AxiosError) { if (error.isAxiosError) { - return error.response.data.message ?? FAILURE_MESSAGES.UNEXPECTED_ERROR; + // response might not be present if there is a network error + const { response } = error; + if (response) { + return response?.data?.message ?? FAILURE_MESSAGES.UNEXPECTED_ERROR; + } else { + return error.message; + } } } return FAILURE_MESSAGES.UNEXPECTED_ERROR; diff --git a/src/env.d.ts b/src/env.d.ts index 424a6f05..cc0d6e4e 100644 --- a/src/env.d.ts +++ b/src/env.d.ts @@ -3,6 +3,7 @@ interface ImportMetaEnv { readonly VITE_PORT: number; readonly VITE_VERSION: string; + readonly VITE_GRAASP_API_HOST: string; readonly VITE_GRAASP_DOMAIN: string; readonly VITE_GRAASP_AUTH_HOST: string; readonly VITE_GRAASP_BUILDER_HOST: string; diff --git a/src/langs/constants.ts b/src/langs/constants.ts index 0615adc9..dca6946d 100644 --- a/src/langs/constants.ts +++ b/src/langs/constants.ts @@ -34,4 +34,10 @@ export const AUTH = { USER_AGREEMENTS_CHECKBOX_LABEL: 'USER_AGREEMENTS_CHECKBOX_LABEL', TERMS_OF_SERVICE_LINK: 'TERMS_OF_SERVICE_LINK', PRIVACY_POLICY_LINK: 'PRIVACY_POLICY_LINK', + INVITATIONS_LOADING_MESSAGE: 'INVITATIONS_LOADING_MESSAGE', + USERNAME_TOO_SHORT_ERROR: 'USERNAME_TOO_SHORT_ERROR', + USERNAME_TOO_LONG_ERROR: 'USERNAME_TOO_LONG_ERROR', + INVALID_EMAIL_ERROR: 'INVALID_EMAIL_ERROR', + EMPTY_EMAIL_ERROR: 'EMPTY_EMAIL_ERROR', + PASSWORD_EMPTY_ERROR: 'PASSWORD_EMPTY_ERROR', }; diff --git a/src/langs/en.json b/src/langs/en.json index b2614b97..52e83464 100644 --- a/src/langs/en.json +++ b/src/langs/en.json @@ -32,5 +32,11 @@ "USER_AGREEMENTS_PRIVACY_POLICY": "privacy policy", "USER_AGREEMENTS_CHECKBOX_LABEL": "By clicking on <0>{{sign_up_btn}}, I agree to the <1>{{terms_of_service}} and the <2>{{privacy_policy}}.", "TERMS_OF_SERVICE_LINK": "/terms", - "PRIVACY_POLICY_LINK": "/privacy" + "PRIVACY_POLICY_LINK": "/privacy", + "INVITATIONS_LOADING_MESSAGE": "We are looking for your invitation, please stand by…", + "USERNAME_TOO_SHORT_ERROR": "Please enter a username with more than {{min}} characters", + "USERNAME_TOO_LONG_ERROR": "Please enter a username under {{max}} characters", + "INVALID_EMAIL_ERROR": "This does not look like a valid email address", + "EMPTY_EMAIL_ERROR": "An email address is required, this field can not be empty", + "PASSWORD_EMPTY_ERROR": "The password can not be empty" } diff --git a/src/utils/validation.ts b/src/utils/validation.ts index 5aa6ebfd..11dd18c9 100644 --- a/src/utils/validation.ts +++ b/src/utils/validation.ts @@ -1,20 +1,24 @@ import validator from 'validator'; -import { NAME_MAXIMUM_LENGTH, NAME_MINIMUM_LENGTH } from '../config/constants'; -import { - EMPTY_EMAIL_ERROR, +import { MAX_USERNAME_LENGTH, MIN_USERNAME_LENGTH } from '@graasp/sdk'; + +import { AUTH } from '../langs/constants'; + +const { + USERNAME_TOO_LONG_ERROR, + USERNAME_TOO_SHORT_ERROR, INVALID_EMAIL_ERROR, PASSWORD_EMPTY_ERROR, - USERNAME_ERROR_MAXIMUM_MESSAGE, - USERNAME_ERROR_MINIMUM_MESSAGE, -} from '../config/messages'; + EMPTY_EMAIL_ERROR, +} = AUTH; export const nameValidator = (name: string) => { - if (name.length > NAME_MAXIMUM_LENGTH) { - return USERNAME_ERROR_MAXIMUM_MESSAGE; + const trimmedName = name.trim(); + if (trimmedName.length > MAX_USERNAME_LENGTH) { + return USERNAME_TOO_LONG_ERROR; } - if (name.length < NAME_MINIMUM_LENGTH) { - return USERNAME_ERROR_MINIMUM_MESSAGE; + if (trimmedName.length < MIN_USERNAME_LENGTH) { + return USERNAME_TOO_SHORT_ERROR; } return null; }; diff --git a/yarn.lock b/yarn.lock index a82bb22a..075e0078 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3073,9 +3073,9 @@ __metadata: languageName: node linkType: hard -"@graasp/sdk@npm:4.2.1": - version: 4.2.1 - resolution: "@graasp/sdk@npm:4.2.1" +"@graasp/sdk@npm:4.7.2": + version: 4.7.2 + resolution: "@graasp/sdk@npm:4.7.2" dependencies: "@faker-js/faker": "npm:8.4.1" js-cookie: "npm:3.0.5" @@ -3083,7 +3083,7 @@ __metadata: peerDependencies: date-fns: ^3 uuid: ^9 - checksum: 10/04e00d13a806ef62183b7314d39345a001981b31754d6ca5f61b80bd919a3de42d3310390bdc5834c0c03f95cf5480fb23d4a06da0a4cd4f3b67f01e067349a0 + checksum: 10/f37de1eb4fb02fa748a00e630f8f133d937fd9eb0aa0016a6e071fa37a44d1599be61ff8754a03f77bb5996f9aebf8e6c969fdb52f4463111c71f6ce6d1fcb38 languageName: node linkType: hard @@ -3096,9 +3096,9 @@ __metadata: languageName: node linkType: hard -"@graasp/ui@npm:4.11.0": - version: 4.11.0 - resolution: "@graasp/ui@npm:4.11.0" +"@graasp/ui@npm:4.14.2": + version: 4.14.2 + resolution: "@graasp/ui@npm:4.14.2" dependencies: "@ag-grid-community/client-side-row-model": "npm:31.1.1" "@ag-grid-community/react": "npm:^31.1.1" @@ -3106,14 +3106,14 @@ __metadata: "@storybook/react-vite": "npm:7.6.17" http-status-codes: "npm:2.3.0" interweave: "npm:13.1.0" - katex: "npm:0.16.9" + katex: "npm:0.16.10" lodash.truncate: "npm:4.4.2" react-cookie-consent: "npm:9.0.0" react-quill: "npm:2.0.0" react-rnd: "npm:10.4.1" react-text-mask: "npm:5.5.0" uuid: "npm:9.0.1" - vitest: "npm:1.3.1" + vitest: "npm:1.4.0" peerDependencies: "@emotion/cache": ~11.10.7 || ~11.11.0 "@emotion/react": ~11.10.6 || ~11.11.0 @@ -3130,7 +3130,7 @@ __metadata: react-router-dom: ^6.11.0 stylis: ^4.1.3 stylis-plugin-rtl: ^2.1.1 - checksum: 10/4fefdb4e23d0f4838cfbd895512ed55b2757167edee68f3d76220b130cff4bbb7b093480a32f361be63d0ff8d74f5ca8b7a6367156d13060fa24d0e6876797f5 + checksum: 10/b4560e2b0b4a5b942dd3ad10029aac7dd7cd8817231d86b380867b5ab758589fe07e73816d42ea85b30830e3db53c9ecf936facf290d18dbd0a5afde44a1a085 languageName: node linkType: hard @@ -5043,57 +5043,57 @@ __metadata: languageName: node linkType: hard -"@vitest/expect@npm:1.3.1": - version: 1.3.1 - resolution: "@vitest/expect@npm:1.3.1" +"@vitest/expect@npm:1.4.0": + version: 1.4.0 + resolution: "@vitest/expect@npm:1.4.0" dependencies: - "@vitest/spy": "npm:1.3.1" - "@vitest/utils": "npm:1.3.1" + "@vitest/spy": "npm:1.4.0" + "@vitest/utils": "npm:1.4.0" chai: "npm:^4.3.10" - checksum: 10/7c2818b2080ec107cffcc1566195132695c8e87cba883e878c2f36ac4d8107bb0a1f8d3823ccc0da5989e245ea114b8afffe790512aebcde8537ac8c1bcf3454 + checksum: 10/00d794a807b7e496d8450133430c8528d4b6cfaba9520bf49640c941b14acaa7b28f151c249b44d935740cae887f0648980db63f38e37bdeb6c2906387e15188 languageName: node linkType: hard -"@vitest/runner@npm:1.3.1": - version: 1.3.1 - resolution: "@vitest/runner@npm:1.3.1" +"@vitest/runner@npm:1.4.0": + version: 1.4.0 + resolution: "@vitest/runner@npm:1.4.0" dependencies: - "@vitest/utils": "npm:1.3.1" + "@vitest/utils": "npm:1.4.0" p-limit: "npm:^5.0.0" pathe: "npm:^1.1.1" - checksum: 10/dcd452216b83406df49e298da5e49dbc45c542f755a726a132461b94a84c386b6cabcdfce17fc2d31fa66fd5ea4e57be3618c7dc627b17791e2a1a065d919616 + checksum: 10/7b8a692de5cef72ef698e83eb5bbb89076924e7a557ed087e80c5080e000a575f34c481f3b880aa2588da5a095504dc55216c319f6924eddfcfc3412f10a27b2 languageName: node linkType: hard -"@vitest/snapshot@npm:1.3.1": - version: 1.3.1 - resolution: "@vitest/snapshot@npm:1.3.1" +"@vitest/snapshot@npm:1.4.0": + version: 1.4.0 + resolution: "@vitest/snapshot@npm:1.4.0" dependencies: magic-string: "npm:^0.30.5" pathe: "npm:^1.1.1" pretty-format: "npm:^29.7.0" - checksum: 10/2212ae82eb8d458ddaa6c28c7e33b6a8c8897e298b88e458bf83e7f9bf767fd716ed507f3cd41ebbe145d59baa72220e9f494552f92cc22b39241dc32b8ad8e1 + checksum: 10/43e22f8aeef4b87bcce79b37775415d4b558e32d906992d4a0acbe81c8e84cbfe3e488dd32c504c4f4d8f2c3f96842acb524b4b210036fda6796e64d0140d5f6 languageName: node linkType: hard -"@vitest/spy@npm:1.3.1": - version: 1.3.1 - resolution: "@vitest/spy@npm:1.3.1" +"@vitest/spy@npm:1.4.0": + version: 1.4.0 + resolution: "@vitest/spy@npm:1.4.0" dependencies: tinyspy: "npm:^2.2.0" - checksum: 10/544c8a30fdeb32fb7bf2c2b5816519be943f5ef90668c306b14efdde7676771d0e83cf0e0a5c79fad722be3839432226bcf74173110a032299821e00b67f47e6 + checksum: 10/0e48f9a64f62801c2abf10df1013ec5e5b75c47bdca6a5d4c8246b3dd7bdf01ade3df6c99fd0751a870a16bd63c127b3e58e0f5cbc320c48d0727ab5da89d028 languageName: node linkType: hard -"@vitest/utils@npm:1.3.1": - version: 1.3.1 - resolution: "@vitest/utils@npm:1.3.1" +"@vitest/utils@npm:1.4.0": + version: 1.4.0 + resolution: "@vitest/utils@npm:1.4.0" dependencies: diff-sequences: "npm:^29.6.3" estree-walker: "npm:^3.0.3" loupe: "npm:^2.3.7" pretty-format: "npm:^29.7.0" - checksum: 10/170c62e6c348562f611d8caddc893e8cba75ed89986e09aa2f0fe6812c96664e8d0f6e329f7a96a4c9cdecf147f4853e4054c3db597b111ec993d3cdd546eddc + checksum: 10/2261705e2edc10376f2524a4bf6616688680094d94fff683681a1ef8d3d59271dee2d80893efad8e6437bbdb00390e2edd754d94cf42100db86f2cfd9c44826f languageName: node linkType: hard @@ -8930,9 +8930,9 @@ __metadata: "@emotion/react": "npm:11.11.4" "@emotion/styled": "npm:11.11.0" "@graasp/query-client": "npm:3.0.2" - "@graasp/sdk": "npm:4.2.1" + "@graasp/sdk": "npm:4.7.2" "@graasp/translations": "npm:1.25.3" - "@graasp/ui": "npm:4.11.0" + "@graasp/ui": "npm:4.14.2" "@mui/icons-material": "npm:5.15.14" "@mui/lab": "npm:5.0.0-alpha.169" "@mui/material": "npm:5.15.14" @@ -10146,14 +10146,14 @@ __metadata: languageName: node linkType: hard -"katex@npm:0.16.9": - version: 0.16.9 - resolution: "katex@npm:0.16.9" +"katex@npm:0.16.10": + version: 0.16.10 + resolution: "katex@npm:0.16.10" dependencies: commander: "npm:^8.3.0" bin: katex: cli.js - checksum: 10/0a1ea1e87c286f845ccd12adadb39e5bc6655c830229195697e5ed74b525f3a4595e77ed3de57d3cfc678476c4bc074ee421fc2dc156554a11968f9d9b5093ea + checksum: 10/367034012311c695791de4553b3e4c7a9f36d126a0cae17b97f4e8832ced2559961f9fa6d39e0116e1374013e12ac8af159eb014678f06b4acf5e547292ea3e5 languageName: node linkType: hard @@ -14064,9 +14064,9 @@ __metadata: languageName: node linkType: hard -"vite-node@npm:1.3.1": - version: 1.3.1 - resolution: "vite-node@npm:1.3.1" +"vite-node@npm:1.4.0": + version: 1.4.0 + resolution: "vite-node@npm:1.4.0" dependencies: cac: "npm:^6.7.14" debug: "npm:^4.3.4" @@ -14075,7 +14075,7 @@ __metadata: vite: "npm:^5.0.0" bin: vite-node: vite-node.mjs - checksum: 10/d6ca8cd5b84768f9a0f12d4327a6aca5200001bd9111991df89ad742e059566a95f8298047af7144ca128120fc67cbd7d37b3fda9700ab07f8fc130d2ad58665 + checksum: 10/691e828c2abe6b62d44183c4e04bdfd119fed405439126fbdc5bfb791644baee3961c1ce429a67b360cc3d8b7c472160c7e82c59491f044a232b4ff480d8a2a2 languageName: node linkType: hard @@ -14225,15 +14225,15 @@ __metadata: languageName: node linkType: hard -"vitest@npm:1.3.1": - version: 1.3.1 - resolution: "vitest@npm:1.3.1" +"vitest@npm:1.4.0": + version: 1.4.0 + resolution: "vitest@npm:1.4.0" dependencies: - "@vitest/expect": "npm:1.3.1" - "@vitest/runner": "npm:1.3.1" - "@vitest/snapshot": "npm:1.3.1" - "@vitest/spy": "npm:1.3.1" - "@vitest/utils": "npm:1.3.1" + "@vitest/expect": "npm:1.4.0" + "@vitest/runner": "npm:1.4.0" + "@vitest/snapshot": "npm:1.4.0" + "@vitest/spy": "npm:1.4.0" + "@vitest/utils": "npm:1.4.0" acorn-walk: "npm:^8.3.2" chai: "npm:^4.3.10" debug: "npm:^4.3.4" @@ -14247,13 +14247,13 @@ __metadata: tinybench: "npm:^2.5.1" tinypool: "npm:^0.8.2" vite: "npm:^5.0.0" - vite-node: "npm:1.3.1" + vite-node: "npm:1.4.0" why-is-node-running: "npm:^2.2.2" peerDependencies: "@edge-runtime/vm": "*" "@types/node": ^18.0.0 || >=20.0.0 - "@vitest/browser": 1.3.1 - "@vitest/ui": 1.3.1 + "@vitest/browser": 1.4.0 + "@vitest/ui": 1.4.0 happy-dom: "*" jsdom: "*" peerDependenciesMeta: @@ -14271,7 +14271,7 @@ __metadata: optional: true bin: vitest: vitest.mjs - checksum: 10/41a8405d65b3b3a48b97b020fae50bdc928a8ebb55b4d24e8867e1ba23d298ea810b37ca6530ab87f6b51d3dc1ada1e067e7bc252a99d8f5439ca4ce6831d1f6 + checksum: 10/cf4675657f4a9ea755d0af70d62827fca9daee64e81d0392067c70a0d1f5f8fd4a47523e28ecf42d667e4d4d7c68b09d5e08389d4b58dc36065364f6c76cda7d languageName: node linkType: hard