diff --git a/.gitignore b/.gitignore index 4d29575..532eddc 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,5 @@ npm-debug.log* yarn-debug.log* yarn-error.log* + +.env \ No newline at end of file diff --git a/env.example b/env.example new file mode 100644 index 0000000..f1c1d4c --- /dev/null +++ b/env.example @@ -0,0 +1 @@ +REACT_APP_RECAPTCHA_SITE_KEY= \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index bddf831..af185bb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,7 @@ "react": "^18.2.0", "react-countup": "^6.5.0", "react-dom": "^18.2.0", + "react-google-recaptcha": "^3.1.0", "react-i18next": "^13.0.1", "react-microsoft-clarity": "^1.2.0", "react-qr-scanner": "^1.0.0-alpha.11", @@ -11180,6 +11181,19 @@ "he": "bin/he" } }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/hoist-non-react-statics/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, "node_modules/hoopy": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", @@ -18496,6 +18510,18 @@ "node": ">=14" } }, + "node_modules/react-async-script": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/react-async-script/-/react-async-script-1.2.0.tgz", + "integrity": "sha512-bCpkbm9JiAuMGhkqoAiC0lLkb40DJ0HOEJIku+9JDjxX3Rcs+ztEOG13wbrOskt3n2DTrjshhaQ/iay+SnGg5Q==", + "dependencies": { + "hoist-non-react-statics": "^3.3.0", + "prop-types": "^15.5.0" + }, + "peerDependencies": { + "react": ">=16.4.1" + } + }, "node_modules/react-countup": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/react-countup/-/react-countup-6.5.0.tgz", @@ -18664,6 +18690,18 @@ "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" }, + "node_modules/react-google-recaptcha": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/react-google-recaptcha/-/react-google-recaptcha-3.1.0.tgz", + "integrity": "sha512-cYW2/DWas8nEKZGD7SCu9BSuVz8iOcOLHChHyi7upUuVhkpkhYG/6N3KDiTQ3XAiZ2UAZkfvYKMfAHOzBOcGEg==", + "dependencies": { + "prop-types": "^15.5.0", + "react-async-script": "^1.2.0" + }, + "peerDependencies": { + "react": ">=16.4.1" + } + }, "node_modules/react-hook-form": { "version": "7.48.2", "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.48.2.tgz", diff --git a/package.json b/package.json index bab02f4..5ea59a5 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "react": "^18.2.0", "react-countup": "^6.5.0", "react-dom": "^18.2.0", + "react-google-recaptcha": "^3.1.0", "react-i18next": "^13.0.1", "react-microsoft-clarity": "^1.2.0", "react-qr-scanner": "^1.0.0-alpha.11", diff --git a/public/locales/de/translation.json b/public/locales/de/translation.json index 5518dc6..456a8aa 100644 --- a/public/locales/de/translation.json +++ b/public/locales/de/translation.json @@ -62,7 +62,8 @@ "calendarMaxFutureBookingDaysRequired": "Maximaler Buchungszeitraum ist erforderlich", "calendarMinEarliestBookingTimeRequired": "Minimaler frühester Buchungszeitpunkt ist erforderlich", "companyNameRequired": "Firmenname ist erforderlich", - "companyNameMinLength": "Firmenname muss mindestens {{minLength}} Zeichen lang sein" + "companyNameMinLength": "Firmenname muss mindestens {{minLength}} Zeichen lang sein", + "recaptchaRequired": "Bitte bestätigen Sie, dass Sie kein Roboter sind" }, "request": { "inputsInvalid": { diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index 0127296..52ac929 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -62,7 +62,8 @@ "calendarMaxFutureBookingDaysRequired": "Please enter the max. future booking days", "calendarMinEarliestBookingTimeRequired": "Please enter the min. earliest booking time", "companyNameRequired": "Please enter the company name", - "companyNameMinLength": "Company name must be at least {{minLength}} characters" + "companyNameMinLength": "Company name must be at least {{minLength}} characters", + "recaptchaRequired": "Please confirm that you are not a robot" }, "request": { "inputsInvalid": { diff --git a/src/Pages/Authentication/index.js b/src/Pages/Authentication/index.js index 64eb064..f67eccd 100644 --- a/src/Pages/Authentication/index.js +++ b/src/Pages/Authentication/index.js @@ -17,7 +17,7 @@ import { showInputsInvalidNotification, showUnkownErrorNotification, } from "../../utils"; -import { useEffect, useState } from "react"; +import { useEffect, useRef, useState } from "react"; import { MyAccountNameFormInput, MyCompanyNameFormInput, @@ -27,6 +27,7 @@ import { import { useTranslation } from "react-i18next"; import MyAppLogo from "../../Components/MyAppLogo"; import { useNavigate } from "react-router-dom"; +import ReCAPTCHA from "react-google-recaptcha"; export const AuthenticationMethod = { LOGIN: 1, @@ -95,6 +96,7 @@ function Login({ notificationApi }) { const [isRequesting, setIsRequesting] = useState(false); const [step, setStep] = useState(LoginStep.ACCOUNT_NAME); + const recaptchaValueRef = useRef(null); const [form] = Form.useForm(); @@ -212,6 +214,21 @@ function Login({ notificationApi }) { + + + (recaptchaValueRef.current = value)} + /> +